2016——3——16 kmp 7题
1、传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=2725
题目大意:找一个串在另一个串中出现的次数
题解:kmp(纯裸题)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define maxn 1000100 5 int n,fix,ans,i,lens,lent; 6 char s[maxn],t[maxn]; 7 int next[maxn]; 8 void getnext() 9 { 10 fix=0; 11 for (i=2; i<=lent; i++) 12 { 13 while(fix && t[fix+1]!=t[i]) fix=next[fix]; 14 if (t[fix+1]==t[i]) fix++; 15 next[i]=fix; 16 } 17 } 18 int KMP() 19 { 20 int count; 21 fix=0; count=0; 22 for (int i=1; i<=lens; i++) 23 { 24 while (fix && t[fix+1]!=s[i]) fix=next[fix]; 25 if (t[fix+1]==s[i]) fix++; 26 if (fix==lent) 27 { 28 count++; 29 fix=next[fix]; 30 } 31 } 32 return count; 33 } 34 int main() 35 { 36 int z; 37 scanf("%d",&z); 38 for (int zz=1; zz<=z; zz++) 39 { 40 scanf("%s",t+1); 41 scanf("%s",s+1); 42 lens=strlen(s+1); 43 lent=strlen(t+1); 44 memset(next,0,sizeof(next)); 45 getnext(); 46 ans=KMP(); 47 printf("%d\n",ans); 48 } 49 return 0; 50 }
2、传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=2732
题目大意:给你一个字符串,让你求出最大重复周期(最大周期不和本身重合)
题解:kmp或者扩展kmp(但我并未用这种方法),我用的是kmp,但是一直WA,原来是求next数组把while写成了if(手抖毁一生)。
好吧,写题解了:用kmp求出next数组,然后在去递归next[n],因为j=next[next[n]]一直next下去直到其的next为0就ans+=n-j;
这就可以求出不和母串一样的最大重复周期,但是这又有一个问题你在求j时要递归时间就有可能为n^2的所以在每次递归时改变next的值就好了(详见代码);
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define inf 0x7ffffff 5 #define maxn 1000100 6 char s[maxn]; 7 int nnext[maxn]; 8 int n,i,j,fix; 9 long long ans; 10 using namespace std; 11 int main() 12 { 13 scanf("%d",&n); 14 scanf("%s",s+1); 15 fix=0; 16 for (int i=2; i<=n; i++) 17 { 18 while (fix && s[fix+1]!=s[i]) fix=nnext[fix]; 19 if (s[fix+1]==s[i]) fix++; 20 nnext[i]=fix; 21 } 22 ans=0; 23 for (int i=1; i<=n; i++) 24 { 25 int j=i; 26 if (!nnext[j]) continue; 27 while (nnext[nnext[j]]) nnext[j]=nnext[nnext[j]]; 28 ans+=(i-nnext[j]); 29 } 30 printf("%lld\n",ans); 31 }
3、传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=2726
题目大意:给出一个字母组成的矩阵,找出一个最小的子矩阵,使得这个子矩阵的无限复制扩张之后的矩阵包含原来矩阵如:
ABABA
ABABA
他的最小重复子矩阵是AB
题解:还是kmp,只不过要有一点小技巧,就是把一行当作一个元素,那么就有n个元素,然后做kmp找重复子串,那么最小重复子串就为n-next[n],列也做此操作,答案就是
(n-r[n])*(m-c[m]);
1 #include <cstdio> 2 #include <cstring> 3 char s[10005][80], rs[80][10005]; 4 int R[10005], C[10005]; 5 int r, c; 6 7 void get_nextR() 8 { 9 R[0] = -1; 10 int j = -1, i = 0; 11 while(i < r) 12 { 13 if(j == -1 || strcmp(s[i], s[j]) == 0) 14 { 15 i++; 16 j++; 17 R[i] = j; 18 } 19 else 20 j = R[j]; 21 } 22 } 23 24 void get_nextC() 25 { 26 C[0] = -1; 27 int j = -1, i = 0; 28 while(i < c) 29 { 30 if(j == -1 || strcmp(rs[i], rs[j]) == 0) 31 { 32 i++; 33 j++; 34 C[i] = j; 35 } 36 else 37 j = C[j]; 38 } 39 } 40 41 int main() 42 { 43 while(scanf("%d %d", &r, &c) != EOF) 44 { 45 for(int i = 0; i < r; i++) 46 scanf("%s", s[i]); 47 get_nextR(); 48 for(int i = 0; i < r; i++) 49 for(int j = 0; j < c; j++) 50 rs[j][i] = s[i][j]; 51 get_nextC(); 52 printf("%d\n", (r - R[r]) * (c - C[c])); 53 } 54 }
4、传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=2724
题目大意:给定一个字符串,要求找到同时是它前缀也是后缀的字符串的个数,并且输出他们的长度。
题解:理解一下next数组的用法,从next[n]一直往前求next,那么那些点的坐标就是answer。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define maxn 1000100 5 int n,ans,fix; 6 int nnext[maxn]; 7 char s[maxn]; 8 using namespace std; 9 void show(int n) 10 { 11 if (n<=0) return; 12 show(nnext[n]); 13 printf("%d ",n); 14 } 15 int main() 16 { 17 scanf("%d",&n); 18 scanf("%s",s+1); 19 fix=0; 20 for (int i=2; i<=n; i++) 21 { 22 while (fix && s[fix+1]!=s[i]) fix=nnext[fix]; 23 if (s[fix+1]==s[i]) fix++; 24 nnext[i]=fix; 25 } 26 show(n); 27 return 0; 28 }
5、传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=2723
题目大意:
现在给一个字符串A,和另一个字符串B。要你每次从B串中从左到右第第一个A串。
并且从B串中删除它,直到A串不为B串的子串。问需要几次删除操作。
题解:next数组的应用,只不过用一个堆记录那些没匹配成功的字符,在用它进行匹配。
1 #include<cstring> 2 #include<cstdio> 3 #include<cstring> 4 char t[1000100],s[1000100]; 5 int m,n,top,i,fix,ans; 6 int z[1000100],next[1000010],f[1000010]; 7 using namespace std; 8 int main() 9 { 10 scanf("%s%s",t+1,s+1); 11 m=strlen(s+1); n=strlen(t+1); 12 for (fix=0,i=2; i<=n; i++) 13 { 14 while (fix && t[fix+1]!=t[i]) fix=next[fix]; 15 if (t[fix+1]==t[i]) fix++; 16 next[i]=fix; 17 } 18 for (int i=1; i<=m; i++) 19 { 20 fix=f[z[top]]; 21 while (fix && t[fix+1]!=s[i]) fix=next[fix]; 22 if (t[fix+1]==s[i]) fix++; 23 24 if (fix==n) top-=(n-1),ans++; 25 else 26 f[i]=fix, z[++top]=i; 27 } 28 printf("%d\n",ans); 29 }
6、传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=2719
题目大意:给出一个串S,它包含 N 个字符。设 Pi = S [ 1 .. I ] ,对于一些 Pi , 如果Pi能表示成K个字符串相连而成的(K > 1 ,且K尽量大),则打印I和K。
题解:如果i mod(i-next[i]) =0 && (i/(i-next[i])>1 输出答案(i);
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define inf 0x7fffffff 5 int n,ans; 6 int nnext[1000100]; 7 char s[1000100]; 8 using namespace std; 9 int main() 10 { 11 int z=0; 12 while (true) 13 { 14 scanf("%d",&n); 15 if (n==0) break; 16 z++; 17 printf("Test case #%d\n",z); 18 scanf("%s",s+1); 19 ans=0; 20 int fix=0; 21 for (int i=2; i<=n; i++) 22 { 23 while (fix && s[fix+1]!=s[i]) fix=nnext[fix]; 24 if (s[fix+1]==s[i]) fix++; 25 nnext[i]=fix; 26 } 27 for (int i=2; i<=n; i++) 28 { 29 int k=i-nnext[i]; 30 if (i % k==0 && i/k>1) printf("%d %d\n",i,i/k); 31 } 32 printf("\n"); 33 } 34 }
7、传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=2720
题目大意:给你一个字符串,它是由某个字符串不断自我连接形成的。 但是这个字符串是不确定的,现在只想知道它的最短长度是多少.
题解:同上;
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define inf 0x7fffffff 5 int n,ans; 6 int nnext[1000100]; 7 char s[1000100]; 8 using namespace std; 9 int main() 10 { 11 scanf("%d",&n); 12 scanf("%s",s+1); 13 ans=0; 14 int fix=0; 15 for (int i=2; i<=n; i++) 16 { 17 while (fix && s[fix+1]!=s[i]) fix=nnext[fix]; 18 if (s[fix+1]==s[i]) fix++; 19 nnext[i]=fix; 20 } 21 printf("%d\n",n-nnext[n]); 22 }