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 }
View Code

 

  

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

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 }
View Code

 

转载于:https://www.cnblogs.com/HQHQ/p/5285438.html

KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法的C++代码实现:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值