顺序对齐的N^2算法
顺序对齐
考虑两个字符串右对齐的最佳解法。例如,有一个右对齐方案中字符串是AADDEFGGHC和ADCDEGH。
AAD_DEFGGHC
ADCDE__GH_
每一个数值匹配的位置值2分,一段连续的空格值-1分。所以总分是匹配点的2倍减去连续空格的段数,在上述给定的例子中,6个位置(A,D,D,E,G,H)匹配,三段空格,所以得分2*6+(-1)*3=9,注意,我们并不处罚左边的不匹配位置。若匹配的位置是两个不同的字符,则既不得分也不失分。
请你写个程序找出最佳右对齐方案。
输入
输入文件包含两行,每行一个字符串,最长3000个字符。字符全部是大字字母。
输出
一行,为最佳对齐的得分。
样例
输入:
AADDEFGGHC
ADCDEGH
输出:
9
这道题从dp的角度去思考
发现可以硬想出一种方法
dp(i,j)表示第一个串的前i项,和第二个串的前j项所构成右对齐方案中代价最大的量
所以
1.dp(i,j)=dp(i-1,j-1)+(2/0)<这个根据i,j的情况来确定
2.dp(i,j)=dp(i,k)-1(1<=k<j)
3.dp(i,j)=dp(k,j)-1(1<=k<i)
dp(i,j)=max{1,2,3};
然后发现这个算法是错的因为这个算法的时间复杂度为O(n^3)
所以我们要思考如何不枚举“-”号;
基于这个思想,我们想到了继承;
dp(i,j,0,0)表示第一个串的前i项,和第二个串的前j项所构成右对齐方案中i,j右方都没有空格代价最大的量
dp(i,j,1,0)表示第一个串的前i项,和第二个串的前j项所构成右对齐方案中i,右方有空格,而j右方每空格的代价最大的量
dp(i,j,0,1)同上
那么状态如何转移呢
我认为大家都想到了。
然后就是代码,用迭代实现
//#include<cstdio> //#include<algorithm> //#include<cstring> //#define N 3000+10 //using namespace std; //int f[N][N]; //char s1[N],s2[N]; //int dp(int i,int j) //{ // if(i==0||j==0)return 0; // if(f[i][j])return f[i][j]; // if(s1[i]==s2[j]) f[i][j]=dp(i-1,j-1)+2; // f[i][j]=max(f[i][j],dp(i-1,j-1)); // for(int k=1;k<i;k++)f[i][j]=max(f[i][j],dp(k,j)-1); // for(int k=1;k<j;k++)f[i][j]=max(f[i][j],dp(i,k)-1); // return f[i][j]; // //} //int main() //{ // scanf("%s%s",s1+1,s2+1); // int len1=strlen(s1+1),len2=strlen(s2+1); //// for(int i=1;i<=len1;i++) //// for(int j=1;j<=len2;j++) //// { //// if(f[i][j]){printf("%d %d:",i,j);printf("%d\n",f[i][j]);} //// } // printf("%d",dp(len1,len2)); // return 0; //} #include<cstdio> #include<cstring> #include<algorithm> #define N 3000+10 using namespace std; char a[N],b[N]; int f[N][N][2][2],l1,l2; int main() { scanf("%s%s",a+1,b+1); l1=strlen(a+1); l2=strlen(b+1); f[0][0][0][0] = 1; for(int i=1;i<=l1;i++) for(int j=1;j<=l2;j++) { int t[10]; for (int k = 0; k < 6; ++k) t[k] = -1e9; if(a[i]==b[j]) { t[0]=f[i-1][j-1][0][0]+2; t[1]=f[i-1][j-1][1][0]+2; t[2]=f[i-1][j-1][0][1]+2; } if(a[i]!=b[j]) { t[3]=f[i-1][j-1][0][0]; t[4]=f[i-1][j-1][1][0]; t[5]=f[i-1][j-1][0][1]; } t[6]=f[i][j-1][0][0]-1; t[7]=f[i][j-1][1][0]; t[8]=f[i-1][j][0][0]-1; t[9]=f[i-1][j][0][1]; for(int k=0;k<=5;k++) f[i][j][0][0]=max(f[i][j][0][0],t[k]); f[i][j][1][0]=max(t[6],t[7]); f[i][j][0][1]=max(t[8],t[9]); } int ans=max(f[l1][l2][0][0],max(f[l1][l2][1][0],f[l1][l2][0][1])); printf("%d",ans); return 0; }
这种想法很重要,运用范围很广,主要就是增加半维来使相同的枚举量减少