后来我发现其实第一个字符的列数可以不用枚举,我们先求出list[i][j]表示目标串中第i行能不能从给出的木桩中从位置j开始拼出,(枚举了答案之后可以发现,如果当前答案是i,那么1,1+i,1+2*i.../2,2+i.../之类的肯定在一行,所以每一行的字符就知道了,与实际想象的不同,我这里的第一行指的是1,1+i,2+i...,其实实际上这一行并不一定在你拼出木桩的第一行。然后还没有结束,因为要匹配成功必须同时让n行匹配上,我们发现一个匹配位置的序列a[1]...a[i],能够成功匹配出答案当且仅当只存在一个位置j(可以不存在),使得a[j]=a[j+1]...=a[i]=a[j-1]+1=a[j-2]+1...a[1]+1.,然后可以根据这个dp,设计状态f[i][j][0/1]表示第i行的串与位置j匹配,是否已经加了1,这个方案是否可行,转移就可以了。比如样例,zenit当答案枚举到3的时候第一行是zi,第二行是et,第三行是n,然后原来的木桩有tiet,oink,ezin,list[1][2]=true因为zi可以和第三个木桩从第二列开始匹配成功,同理list[2][3],list[3][3],list[3][4]都等于true。然后dp的时候一开始f[1][2][0]=true,转移方向是f[1][2][0]<--f[2][3][1]<--f[3][3][1].因为f[3][3][1]等于true所以说明答案等于3的时候有解。
(看那些几十毫秒的估计有n^2做法,但是我不会....)
#include <iostream> #include <cstring> #include <cstdlib> #include <string> #include <cstdio> #include <algorithm> #include <cmath> #include <ctime> using namespace std; int tot = 0,m,son[500010][27],n,root[310],_n; char s[310],c[310]; string str[310]; bool list[310][310],f[310][310][2]; void insert(int &x,int y) { if(x == 0) x = ++tot; int Now = x; for(int i = y;i <= m;i ++) { int t = s[i] - 'a'; if(son[Now][t] == 0) { son[Now][t] = ++tot; Now = son[Now][t]; } else Now = son[Now][t]; } } int check(int Now,string x) { int len = x.size(); for(int i = 1;i <= len;i ++) { int t = x[i - 1] - 'a'; if(son[Now][t]) Now = son[Now][t]; else return false; } return true; } int main() { scanf("%d%d",&n,&m); for(int i = 1;i <= n;i ++) { scanf("%s",s + 1); for(int j = 1;j <= m;j ++) insert(root[j],j); } scanf("%s",c + 1); _n = strlen(c + 1); for(int i = 1;i <= _n;i ++) { for(int k = 1;k <= i;k ++) str[k].clear(); for(int j = 1;j <= i;j ++) { for(int k = j;k <= _n;k += i) str[j] += c[k]; } for(int j = 1;j <= i;j ++) { for(int k = 1;k <= m;k ++) { if(check(root[k],str[j])) list[j][k] = true; else list[j][k] = false; } } memset(f,false,sizeof(f)); for(int j = 1;j <= m;j ++) if(list[1][j]) f[1][j][0] = true; for(int j = 2;j <= i;j ++) { for(int k = 1;k <= m;k ++) if(list[j][k]) { if(f[j - 1][k][0]) f[j][k][0] = true; if(f[j - 1][k][1] || f[j - 1][k - 1][0]) f[j][k][1] = true; } } for(int j = 1;j <= m;j ++) if(f[i][j][0] || f[i][j][1]) {cout<<i;return 0;} } }