顺序对齐的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;
}

这种想法很重要,运用范围很广,主要就是增加半维来使相同的枚举量减少

posted @ 2017-09-19 15:46  star_eternal  阅读(459)  评论(0编辑  收藏  举报