2014/11/24
如果有两个字符串,让你求出它们的最长公共子序列。我想你们都会做的。
不过问题不仅限于此,WaKing还想让你告诉他,这两个字符串一共有多少个公共子序列,重复的不算。
输入:
输入两行每行一个字符串,为了使问题简单化,WaKing保证输入的字符在a~z之间,并且字符串的长度不超过50.
输出:
一行它们公共子序列的数量。
保证答案在int范围内。
样例输入:
asd
asd
ass
zas
样例输出:
7
3
题目是我自己编的,参照codevs上的容斥是三个字符串(有点....要用大数....就是diao。。)所以我这里把数据改弱了,直接int就能做,单纯的容斥,怕不怕?
同前文容斥原理一样,字符串两个的时候求公共子序列的数量
那么存在 dp[ i ][ j ] 表示 s1[ i ] == s2[ j ];(不难理解,公共子序列的意义!)
dp[ i ][ j ] =2 * dp[ i -1][j-1] - dp[ ki - 1 ][ kj - 1 ];(s1[ ki ] == s2[ kj ];)(想想为什么?单个字符串求子序列的时候的s[ i ] = 2*s[i-1 ] - s[k - 1])
容斥原理的本质都是在继承状态的过程中消去之前重复计算的一部分。
剩下的就是一些边界处理,初始化,以及 s[ i ] != s[ j ] 的时候的处理(状态继承)
#include<stdio.h>
#include<string.h>
int Max(int a,int b)
{
return a>b?a:b;
}
char s1[55],s2[55];
int Dp[100][100];
int main()
{
while(~scanf("%s%s",s1+1,s2+1))
{
int len1 = strlen(s1+1);
int len2 = strlen(s2+1);
memset(Dp,0,sizeof(Dp));
for(int i = 0;i <= len1; i++)Dp[i][0] =1;
for(int j = 0;j <= len2; j++)Dp[0][j] =1;
for(int i = 1;i <= len1; i++)
for(int j = 1;j <= len2; j++)
{
if(s1[i] == s2[j])
{
int ki = i - 1;while(s1[i]!=s1[ki]&&ki>0)ki--;
int kj = j - 1;while(s2[j]!=s2[kj]&&kj>0)kj--;
Dp[i][j] = 2*Dp[i-1][j-1];
if(ki&&kj)Dp[i][j]-=Dp[ki-1][kj-1];
}
else
{
Dp[i][j] = Dp[i][j-1]+Dp[i-1][j]-Dp[i-1][j-1];//这里也是容斥,状态继承看你高中学的如何咯。
}
}
printf("%d\n",Dp[len1][len2] - 1);
}
return 0;
}