http://www.elijahqi.win/archives/560
题目描述
一个字符串的子串是在这个串基础上去掉0个或者若干个字符所形成的,例如abc, aa和abbc都是字符串aabbcc的子串,而aba不是。 现给你三个字符串,请问他们三个共同含有多少种子串(不算空串)?
注意: 有些相同的公共子串尽管出现在不同的位置,但仍算1种,详见样例。
输入输出格式
输入格式:
每组测试数据只有3行,每行都是一个只包含有小写字母的字符串。
输出格式:
输出3个字符串共有的公共子串种类数。
输入输出样例
输入样例#1:
apartment
apache
approach
输出样例#1:
6
说明
3个字符串共有的公共子串有: “a”, “p”, “ap”, “pa”, “aa”, “apa”。 其中子串 “a” 有多个,但由于统计的是公共子串种类,所以 “a” 只算1种子串。
100%的数据中,字符串的长度不超过100。字符串中只含有小写字母。
在所有数据中,答案均小于2^30。
方案数可能超int。这题坑了好久。f[i][j][k]表示s1前i个字母,s2前j个字母,s3前k个字母所包含的本质不同的公共子串的个数。为了避免重复,我们每次做i,j,k时都重新统计所有的本质不同的公共子串。怎么统计呢?枚举公共子串的结尾为a…z,假设当前枚举a,找到目前为止的每一个串的最后一个a的位置,为了避免重复,我们把所有满足以a结尾的公共子串的结尾都当做是这个a,则个数为f[ii-1][jj-1][kk-1]+1。每次都重新统计,最后答案就是f[n1][n2][n3]。
举个例子 :要求f[i][j][k] 枚举b时 找到最后一次出现b的位置 f[i’][j’][k’] 那么 前面有的bb aa ab cd等串
都都会加上现在的b形成新的串 bbb也是一个新的子串 那么c d 等结尾的就是后面继续循环的时候可以找到
#include<cstdio>
#include<cstring>
#define N 110
long long data[N][N][N];
char a[N],b[N],c[N];
int last1[N][30],last2[N][30],last3[N][30];
void f(int i,int j,int k){
long long ans=0;
//data[i][j][k]=0;
for (int z=0;z<=25;++z){
int x1=last1[i][z],x2=last2[j][z],x3=last3[k][z];
if (x1==0||x2==0||x3==0) continue;
if (data[x1-1][x2-1][x3-1]==-1) f(x1-1,x2-1,x3-1);
ans+=data[x1-1][x2-1][x3-1]+1;
}
data[i][j][k]=ans;
}
int main(){
// freopen("3856.in","r",stdin);
scanf("%s%s%s",a+1,b+1,c+1);
int n=strlen(a+1),m=strlen(b+1),k=strlen(c+1);
for (int i=1;i<=n;++i)
for (int j=0;j<26;++j) {last1[i][j]=last1[i-1][j];if(a[i]=='a'+j) last1[i][j]=i;}
for (int i=1;i<=m;++i)
for (int j=0;j<26;++j){ last2[i][j]=last2[i-1][j];if (b[i]=='a'+j) last2[i][j]=i;}
for (int i=1;i<=k;++i)
for (int j=0;j<26;++j){ last3[i][j]=last3[i-1][j];if (c[i]=='a'+j) last3[i][j]=i;}
memset(data,0xff,sizeof(data));
//data[0][0][0]=1;
f(n,m,k);
printf("%lld",data[n][m][k]);
return 0;
}