KMP的循环节问题总结 ###K

len=i-next[i] 等价于 以i为终点的最小循环节长度

假设 总长为n  如果len能整除n 说明周期为n/len 

否则  差几个就补几个就能达到一个周期 即 ans= (len-n%len)%len

 

题目链接:https://vjudge.net/contest/394060#problem/J

题意:给一段字符串 问小数部分后的 a*p-b*l 最大为多少, a b给定, p是该循环节的总长度,l是循环节的一个周期的长度

思路:求循环节 考虑kmp  倒序kmp求出next数组 i-next[i] 即为循环节的一个周期长度 即长度减去公共前后缀的长度= 一个周期的长度

详细理解同第四题

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define pb push_back
 5 const int maxn =1e7+10;
 6 const int mod=1e9+7;
 7 
 8 
 9 char t[maxn];
10 char s[maxn];
11 int nxt[maxn];
12 
13 
14 
15 int main()
16 {
17     ios::sync_with_stdio(false);
18     cin.tie(0);
19     ll a,b;
20     while(cin>>a>>b)
21     {
22         cin>>(t+1);
23         int n=strlen(t+1);
24         int cnt=0;
25         for(int i=n;i>=1;i--)
26         {
27             if(t[i]=='.')
28                 break;
29             s[++cnt]=t[i];
30         }
31 
32         int j=0;
33         for(int i=2;i<=cnt;i++)
34         {
35             while(j&&s[i]!=s[j+1])
36                 j=nxt[j];
37             if(s[i]==s[j+1]) j++;
38             nxt[i]=j;
39         }
40         /*for(int i=1;i<=cnt;i++)
41             cout<<nxt[i]<<'\n';*/
42         ll ans=-1e18;
43         for(int i=1;i<=cnt;i++)
44         {
45             ans=max(ans,a*i-b*(i-nxt[i]));
46         }
47         cout<<ans<<'\n';
48 
49 
50     }
51 
52 
53 
54 
55 
56 }
View Code

 

题目链接:https://vjudge.net/problem/HDU-3746

题意:给定一串珠子 问最少增加多少个珠子使得 这串珠子的循环节个数>=2

思路: len= i-nxt[i] 如果len==n  那么就ans=n 否则  ans= (len-n%len)%len

所以kmp求出next数组后  判断n-next[n] 即可

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e5+10;
 4 const int mod=1e9+7;
 5 #define ll long long
 6 #define ull unsigned long long
 7 #define pi pair<int,int>
 8 #define fi first
 9 #define sc second
10 #define pb push_back
11 
12 char s[maxn];
13 int nxt[maxn];
14 int n;
15 
16 void getnxt()
17 {
18     for(int i=2,j=0;i<=n;i++)
19     {
20         while(j&&s[i]!=s[j+1]) j=nxt[j];
21         if(s[i]==s[j+1]) j++;
22         nxt[i]=j;
23     }
24 }
25 
26 
27 int main()
28 {
29     ios::sync_with_stdio(false);
30     cin.tie(0);
31     int t;
32     cin>>t;
33     while(t--)
34     {
35         cin>>(s+1);
36         n=strlen(s+1);
37         getnxt();
38         int k=n-nxt[n];
39         int ans=0;
40         if(k==n) ans=n;
41         else ans=(k-n%k)%k;
42         cout<<ans<<'\n';
43     }
44 
45 
46 
47 }
View Code

 

 

题目链接:https://www.acwing.com/problem/content/143/

题意:找出字符串所有前缀是否存在循环节

思路:kmp求出next数组后  再去枚举前缀的端点即可

kmp已经把 以i 结尾的 nxt[i] 都求出来了

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e6+10;
 4 const int mod=1e9+7;
 5 #define ll long long
 6 #define ull unsigned long long
 7 #define pi pair<int,ll>
 8 #define fi first
 9 #define sc second
10 #define pb push_back
11 
12 int n;
13 char s[maxn];
14 int nxt[maxn];
15 
16 void getnxt()
17 {
18     for(int i=2,j=0;i<=n;i++)
19     {
20         while(j&&s[i]!=s[j+1]) j=nxt[j];
21         if(s[i]==s[j+1]) j++;
22         nxt[i]=j;
23     }
24 }
25 
26 
27 
28 int main()
29 {
30     ios::sync_with_stdio(0);
31     cin.tie(0);
32 
33     int c=0;
34     while(cin>>n,n)
35     {
36         cin>>(s+1);
37         getnxt();
38         cout<<"Test case #"<<++c<<'\n';
39         for(int i=1;i<=n;i++)
40         {
41             int len=i-nxt[i];
42             if(i%len==0&&i/len!=1)
43             {
44                 cout<<i<<" "<<i/len<<'\n';
45             }
46         }
47         cout<<'\n';
48     }
49 
50 
51 
52 
53 }
View Code

 

 

题目链接:https://ac.nowcoder.com/acm/contest/13506/I

题意:删去一段前缀k后 使得剩余的字符串中每个i满足 T[i]=T[i+p]  越界的认为是满足条件

删去的个数为k  要求k+p最小  如果相等即p取最小

思路:枚举删除的前缀, 然后我们需要可以o1 得出剩下的后缀的 循环节长度p (可以看出p就是循环节长度的含义)

所以需要逆序跑一遍kmp  那么i-nxt[i] 即为循环节长度p   因为越界是满足条件的, 所以是非完全循环节即可 同时注意题目要求p>0 

逆序看起来 可能是向左增加的循环节  如 X X 1 2 3 1  到的时候可能看起来会向左补 3 2  但其实因为公共前后缀的对称关系

向右补 2 3 是等价关系

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e6+10;
 4 const int mod=1e9+7;
 5 #define ll long long
 6 #define ull unsigned long long
 7 #define pi pair<int,ll>
 8 #define fi first
 9 #define sc second
10 #define pb push_back
11 
12 
13 int a[maxn];
14 int nxt[maxn];
15 int n;
16 
17 
18 void get()
19 {
20     for(int i=2,j=0;i<=n;i++)
21     {
22         while(j&&a[j+1]!=a[i]) j=nxt[j];
23         if(a[j+1]==a[i]) j++;
24         nxt[i]=j;
25     }
26 }
27 
28 
29 
30 
31 int main()
32 {
33     ios::sync_with_stdio(0);
34     cin.tie(0);
35     cin>>n;
36     for(int i=1;i<=n;i++) cin>>a[i];
37     reverse(a+1,a+1+n);
38     get();
39 
40     int ans=n,k=n-1,p=1;
41     for(int i=1;i<=n;i++)
42     {
43         int tmpk=n-i;
44         int tmpp=i-nxt[i];
45         int sum=tmpk+tmpp;
46         if(sum<ans||(sum==ans&&tmpp<p))
47         {
48             ans=sum;
49             p=tmpp;
50             k=tmpk;
51         }
52     }
53     cout<<k<<" "<<p<<'\n';
54 
55 
56 
57 
58 
59 
60 
61 }
View Code

 

posted @ 2020-10-12 16:31  canwinfor  阅读(126)  评论(0编辑  收藏  举报