POJ 3167 Cow Patterns (KMP+前缀和)
2016-09-14 21:33
441 查看
题意:给你两串数字,长度分别为n和m,数字大小在[1,25]。当后一串数字每个数字的排名位置与前一串数字(任一长度为m的子串)每个数字的排名位置一致时就完全匹配,最后求哪些位置是完全匹配的。
例如:1 4 2 5 3 6 与 1 3 2 4 答案就是:1 3(第一串数字的第一个位置开始与第三个位置开始)
挺难想的一个题,我们需要使用dp的思想加前缀和进行KMP匹配
首先,两串数字串完全匹配就可以想到KMP,这样我们就只需要解决一个问题:两个数字怎样才算是“相等”(这儿并不是值一样就“相等”)。我们可以这样想,当两串数字对应位置:前面比此位置数字小的,与此位置数字相等的个数都一样,这样就一定匹配,因为这样每个数字排名位置一定相等(注意第一个数字串是子串)。
我们可以注意数字范围很小,所以可以使用前缀和记录:每个位置前面每种数字不大于这个数字大小的个数。这样求Next数组和匹配时直接使用前缀和来处理就好。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<string> #include<cstdio> #include<cstring> #include<stdlib.h> #include<iostream> #include<algorithm> using namespace std; #define eps 1E-8 /*注意可能会有输出-0.000*/ #define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型 #define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化 #define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0 #define mul(a,b) (a<<b) #define dir(a,b) (a>>b) typedef long long ll; typedef unsigned long long ull; const int Inf=1<<28; const double Pi=acos(-1.0); const int Mod=1e9+7; const int Max=100010; int Next[Max],k; struct node//前i个字符中大于等于某种数字的总个数 { int num[Max][27]; }; void Init(int n,int *str,node &dp)//预处理dp { memset(dp.num[0],0,sizeof(dp.num[0])); for(int i=1;i<n;++i) { dp.num[i][0]=0; for(int j=1;j<=k;j++) { dp.num[i][j]=dp.num[i-1][j]; if(j>=str[i-1]) dp.num[i][j]=dp.num[i-1][j]+1; } } return; } int Jud(int i,int j,node &dpm,node &dpp,int *strm,int *strp)//模式串与匹配串是否匹配 { int lim=j-i; if(dpm.num[i][strm[i]]==dpp.num[j][strp[j]]-dpp.num[lim][strp[j]]&&dpm.num[i][strm[i]-1]==dpp.num[j][strp[j]-1]-dpp.num[lim][strp[j]-1])//这儿使用前缀和计算时要注意固定不大于某个值 return 1; return 0; } void GetNext(int m,int *str,node &dp)//求next数组 { Next[0]=-1; int i=-1,j=0; while(j<m) { if(i==-1||Jud(i,j,dp,dp,str,str))//当此字符前面小于等于其的都相等时就匹配 { ++i,++j; Next[j]=i; } else i=Next[i]; } return; } int strm[Max],strp[Max]; node dpm,dpp; int ans[Max],cnt; int Kmp(int n,int m)//标准模式匹配修改 { int res=0; int i=0,j=0; while(j<n) { if(i==-1||Jud(i,j,dpm,dpp,strm,strp)) { ++i,++j; } else i=Next[i]; if(i==m)//匹配成功 { ans[cnt++]=j-i+1; i=Next[i]; res++; } } return res; } int main() { int n,m; while(~scanf("%d %d %d",&n,&m,&k)) { cnt=0; for(int i=0;i<n;++i) scanf("%d",&strp[i]); for(int i=0;i<m;++i) scanf("%d",&strm[i]); Init(m,strm,dpm); GetNext(m,strm,dpm); Init(n,strp,dpp); int len=Kmp(n,m); printf("%d\n",len); for(int i=0;i<len;++i) printf("%d\n",ans[i]); } return 0; }
相关文章推荐
- POJ-3167- Cow Patterns(KMP)
- poj 3167 Cow Patterns(kmp)
- LA 3026 && POJ 1961 Period(KMP求前缀的最短循环节)
- POJ 2752 Seek the Name, Seek the Fame kmp(后缀与前缀)
- poj_2752_kmp_nxt_application_求s 所有子串是s的前缀又是后缀的长度
- HDU 4749 && POJ 3167 KMP
- POJ 3167 Cow Patterns
- HDU 4749 & POJ 3167 kmp变形
- POJ 3167 Cow Patterns 笔记
- POJ 3167 Cow Patterns
- poj 3167 (kmp 好题)
- POJ2406 KMP前缀周期
- POJ 3167 Cow Patterns
- 【POJ 3167】Cow Patterns (KMP+树状数组)
- 【 poj 1961 】Period 【KMP 求所有前缀的循环节】
- POJ 2752 Seek the Name, Seekthe Fame(KMP:后缀与前缀)
- POJ 3167 Cow Patterns KMP+暴力
- POJ 2752 既是前缀又是后缀的子串 (kmp)
- POJ - 2752 Seek the Name, Seek the Fame(前缀和后缀子串相同 KMP)
- POJ 2752 Seek the Name, Seek the Fame (KMP的next函数,求前缀和后缀的匹配长度)