KMP--君住长江头,我住长江尾,日日思君不见君,共饮长江水

POJ 3461: Oulipo

题意:

求出第一个串在第二个串中的出现次数...

分析:

KMP板子题...

代码:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 //by NeighThorn
 6 using namespace std;
 7 //眉眼如初,岁月如故
 8 
 9 const int maxn=1000000+5;
10 
11 int cas,lens,lenp,nxt[maxn];
12 
13 char s[maxn],p[maxn];
14 
15 inline void getnxt(void){
16     nxt[0]=nxt[1]=0;int k;
17     for(int i=1;i<lenp;i++){
18         k=nxt[i];
19         while(k&&p[k+1]!=p[i+1])
20             k=nxt[k];
21         if(p[k+1]==p[i+1])
22             nxt[i+1]=k+1;
23         else
24             nxt[i+1]=0;
25     }
26 }
27 
28 inline void kmp(void){
29     int ans=0,posp=1,poss=1;
30     while(poss<=lens){
31         if(p[posp]==s[poss])
32             posp++,poss++;
33         else if(posp==1)
34             poss++;
35         else
36             posp=nxt[posp-1]+1;
37         if(posp==lenp+1)
38             ans++,posp=nxt[posp-1]+1;
39     }
40     printf("%d
",ans);
41 }
42 
43 signed main(void){
44     scanf("%d",&cas);
45     while(cas--){
46         memset(nxt,0,sizeof(nxt));
47         scanf("%s%s",p+1,s+1);
48         lenp=strlen(p+1);lens=strlen(s+1);
49         getnxt();kmp();    
50     }
51     return 0;
52 }//Cap ou pas cap. Pas cap.

POJ 2406: Power Strings

题意:

求出每个串的最短循环节出现次数...

分析:

此题两种解法...然而本质上是一样的...

No.1 Hash

对于一个串其前缀A和后缀B相同并且有重叠,并且len%strlen(str-B)==0,那么str-B一定是循环节...所以我们可以用hash水过...

No.2 next数组...

然而更机智的做法是next数组...

代码:

No.1 Hash

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #define int long long
 6 using namespace std;
 7 const int maxn=1000000+5,MOD=100000007;
 8 char str[maxn];
 9 int len,ans;
10 unsigned int hash[maxn],pow[maxn]; 
11 signed main(void){
12     pow[0]=(long long)1;
13     for(int i=1;i<maxn;i++)
14         pow[i]=(pow[i-1]*30)%MOD;
15     while(scanf("%s",str+1)&&str[1]!='.'){
16         len=strlen(str+1),hash[0]=0,ans=1;
17         for(int i=1;i<=len;i++)
18             hash[i]=(hash[i-1]*30%MOD+str[i]-'a'+1)%MOD;
19         for(int i=1;i<len;i++){
20             if(len%(len-i)==0){
21                 if(hash[i]%MOD==(hash[len]-hash[len-i]*pow[i]%MOD+MOD)%MOD){
22                     ans=len/(len-i);
23                 }
24             }
25         }
26         cout<<ans<<endl;
27     }
28     return 0;
29 }

No.2 next数组

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 //by NeighThorn
 6 using namespace std;
 7 //眉眼如初,岁月如故
 8 
 9 const int maxn=1000000+5;
10 
11 int len,nxt[maxn];
12 
13 char str[maxn];
14 
15 inline void getnxt(void){
16     nxt[0]=nxt[1]=0;int k;
17     for(int i=1;i<len;i++){
18         k=nxt[i];
19         while(k&&str[k+1]!=str[i+1])
20             k=nxt[k];
21         if(str[k+1]==str[i+1])
22             nxt[i+1]=k+1;
23         else
24             nxt[i+1]=0;
25     }
26 }
27 
28 signed main(void){
29     while(scanf("%s",str+1)&&str[1]!='.'){
30         len=strlen(str+1);getnxt();
31         if(len%(len-nxt[len])==0)
32             printf("%d
",len/(len-nxt[len]));
33         else
34             puts("1");
35     }
36     return 0;
37 }//Cap ou pas cap. Pas cap.

POJ 2752: Seek the Name, Seek the Fame

题意:

输出所有s的合法前缀,合法前缀的定义为前缀等于后缀...

分析:

不断nxt...

代码:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 //by NeighThorn
 6 using namespace std;
 7 //眉眼如初,岁月如故 
 8 
 9 const int maxn=400000+5;
10 
11 int len,tail,nxt[maxn],stk[maxn];
12 
13 char str[maxn];
14 
15 inline void getnxt(void){
16     nxt[0]=nxt[1]=0;int k;
17     for(int i=1;i<len;i++){
18         k=nxt[i];
19         while(k&&str[k+1]!=str[i+1])
20             k=nxt[k];
21         if(str[k+1]==str[i+1])
22             nxt[i+1]=k+1;
23         else
24             nxt[i+1]=0;
25     }
26 }
27 
28 signed main(void){
29     while(scanf("%s",str+1)!=EOF){
30         len=strlen(str+1);getnxt();tail=0;stk[++tail]=len;
31         while(nxt[len])
32             stk[++tail]=nxt[len],len=nxt[len];
33         for(int i=tail;i>=1;i--)
34             printf("%d ",stk[i]);
35         puts("");
36     }
37     return 0;
38 }//Cap ou pas cap. Pas cap.

POJ 3167: Cow Patterns

题意:

给出两个串,如果a和b匹配当且仅当ab中的每个对应位置的数字rank1和rank2相同,rank1代表当前元素前面小于他的元素个数,rank2代表当前元素前面等于他的元素个数,求出匹配串和模式串的匹配位置(开头位置)

 分析:

KMP…裸的KMP…就是计算一下rank就好… 
因为数字不会超过25,所以直接暴力求rank就好…

代码:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 //by NeighThorn
 6 using namespace std;
 7 const int maxn=100000+5,maxk=25000+5,maxs=25+5;
 8 int n,k,s,a[2][maxn],nxt[maxk],num[2][maxn][maxs],vis[maxk];
 9 inline int rank1(int len,int en,int id){
10     int st=en-len,ans=0;
11     for(int i=1;i<a[id][en];i++)
12         ans+=num[id][en][i]-num[id][st][i];
13     return ans;
14 }
15 inline int rank2(int len,int en,int id){
16     return num[id][en][a[id][en]]-num[id][en-len][a[id][en]];
17 }
18 inline void getnext(void){
19     nxt[0]=nxt[1]=0;int p;
20     for(int i=1;i<k;i++){
21         p=nxt[i];int a=rank1(p+1,p+1,1),b=rank1(p+1,i+1,1),c=rank2(p+1,p+1,1),d=rank2(p+1,i+1,1);
22         while(p&&(a!=b||c!=d))
23             p=nxt[p],a=rank1(p+1,p+1,1),b=rank1(p+1,i+1,1),c=rank2(p+1,p+1,1),d=rank2(p+1,i+1,1);
24         if(rank1(p+1,p+1,1)==rank1(p+1,i+1,1)&&rank2(p+1,p+1,1)==rank2(p+1,i+1,1))
25             nxt[i+1]=p+1;
26         else
27             nxt[i+1]=0;
28     }
29 }
30 inline void kmp(void){
31     int posa=1,posb=1,ans=0,stk[maxn];
32     while(posa<=n){
33         if(rank1(posb,posa,0)==rank1(posb,posb,1)&&rank2(posb,posa,0)==rank2(posb,posb,1))
34             posa++,posb++;
35         else if(posb==1)
36             posa++;
37         else
38             posb=nxt[posb-1]+1;
39         if(posb==k+1)
40             stk[++ans]=posa-k,posb=nxt[posb-1]+1;
41     }
42     cout<<ans<<endl;
43     for(int i=1;i<=ans;i++)
44         cout<<stk[i]<<endl;
45 }
46 signed main(void){
47     scanf("%d%d%d",&n,&k,&s);memset(num,0,sizeof(num));
48     for(int i=1;i<=n;i++)
49         scanf("%d",&a[0][i]),num[0][i][a[0][i]]=1;
50     memset(vis,0,sizeof(vis));
51     for(int i=1;i<=n;i++)
52         for(int j=1;j<=s;j++)
53             num[0][i][j]+=num[0][i-1][j];
54     for(int i=1;i<=k;i++)
55         scanf("%d",&a[1][i]),num[1][i][a[1][i]]=1;
56     for(int i=1;i<=k;i++)
57         for(int j=1;j<=s;j++)
58             num[1][i][j]+=num[1][i-1][j];
59     getnext();kmp();
60     return 0;
61 }

By NeighThorn

原文地址:https://www.cnblogs.com/neighthorn/p/6270214.html