TopCoder SRM 298 Div1 Hard CountingCommonSubsequences
問題
三つの文字列a,b,cが与えられる。
a,b,cの全てに(必ずしも連続しない)部分文字列として含まれる文字列はいくつあるか、求めよ。
制約条件
a,b,cはアルファベット小文字からなる。
a,b,cの長さ≦50
方針
動的計画法による。
dp[i][j][k]を、aをi文字目、bをj文字目、cをk文字目まで見たときの部分文字列の個数とする。
ここから、次の一文字を決めればDPテーブルが更新できる。
次の一文字は、aのi文字目以降、bのj文字目以降、cのk文字目以降の全てに含まれている必要がある。
その文字が複数あるときは、最初にでてきた文字だけを数えるようにすれば、漏れも重複もなく部分文字列を数え上げることができる。
ソースコード
int l1,l2,l3,n1[26][51], n2[26][51], n3[26][51]; ll dp[51][51][51]; struct CountingCommonSubsequences{ ll countCommonSubsequences(string a,string b,string c){ calc(n1,a); calc(n2,b); calc(n3,c); l1=a.size(); l2=b.size(); l3=c.size(); dp[0][0][0]=1; ll ans=0; for(int i=0;i<l1;i++)for(int j=0;j<l2;j++)for(int k=0;k<l3;k++)if(dp[i][j][k]){ for(int l=0;l<26;l++){ int ni=n1[l][i], nj=n2[l][j], nk=n3[l][k]; if(ni<0||nj<0||nk<0)continue; dp[ni+1][nj+1][nk+1]+=dp[i][j][k]; ans+=dp[i][j][k]; } } return ans; } void calc(int a[26][51],string s){ int l=s.size(); for(int i=0;i<26;i++)for(int j=0;j<l;j++){ a[i][j]=-1; for(int k=j;k<l;k++)if(s[k]-'a'==i){ a[i][j]=k; break; } } } };