考虑我的每一次添加操作,要满足:1.该串是t的子串;2.该串不能与下一次的串开头字母构成t的子串。那么,设f[i][j][k]表示拼i次,第i次填入的开头字母是j,第i+1填入的开头字母是k的最短长度。
状态转移方程:f[i][j][k]=min(f[i-1][j][t]+f[1][t][k]),这个东西可以用矩阵乘法来搞(把加改成min即可)。
这样求出的最后一个存在j和k满足f[i][j][k]<n的i即为答案,因为f[i][j][k]的值一定随i的增长而增长,因此可以二分i并判断是否存在。
之后还要预处理出f[1][i][j],这个东西暴力枚举显然是$o(n^{2})$的,但是发现判断f[1][i][j]不合法当且仅当所有以i为开头以j为结尾且长度为f[1][i][j]的串都是t的子串,而t中长度固定的串不超过|t|个,而这样的串有$4^{f[1][i][j]}$个,如果满足$4^{f[1][i][j]}>|t|$则f[1][i][j]一定不合法,大约有$f[1][i][j]\leq 20$。
接下来计算就很简单来,可以对s中所有长度小于等于20的子串建一棵trie树并统计一下就可以。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 struct ji{ 5 ll a[5][5]; 6 }a,b,c; 7 ll n,k; 8 int V,tr[2000005][5],f[5][5][100005]; 9 char s[100005]; 10 void ins(int k){ 11 for(int i=k,j=1;(i<k+20)&&(s[i]);j=tr[j][s[i++]-'A']) 12 if (!tr[j][s[i]-'A']){ 13 tr[j][s[i]-'A']=++V; 14 f[s[k]-'A'][s[i]-'A'][i-k]++; 15 } 16 } 17 ji cheng(ji a,ji b){ 18 for(int i=0;i<4;i++) 19 for(int j=0;j<4;j++)c.a[i][j]=n+1; 20 for(int i=0;i<4;i++) 21 for(int j=0;j<4;j++) 22 for(int k=0;k<4;k++) 23 c.a[i][k]=min(c.a[i][k],a.a[i][j]+b.a[j][k]); 24 return c; 25 } 26 void ksm(ji a,ll n){ 27 while (n){ 28 if (n&1)b=cheng(b,a); 29 a=cheng(a,a); 30 n/=2; 31 } 32 } 33 bool pd(ji a){ 34 for(int i=0;i<4;i++) 35 for(int j=0;j<4;j++) 36 if (a.a[i][j]<n)return 1; 37 return 0; 38 } 39 int main(){ 40 scanf("%lld%s",&n,s); 41 V=1; 42 for(int i=0;s[i];i++)ins(i); 43 for(int i=0;i<4;i++) 44 for(int j=0;j<4;j++){ 45 for(int k=1;f[i][j][k]==(1<<(2*k-2));k++)a.a[i][j]=k; 46 a.a[i][j]++; 47 } 48 k=n; 49 for(ll i=0;i<k;){ 50 memset(b.a,0,sizeof(b.a)); 51 ll j=(i+k+1>>1); 52 ksm(a,j); 53 if (pd(b))i=j; 54 else k=j-1; 55 } 56 printf("%lld",k+1); 57 }