NYOJ 37 回文字符串 (dp)
地址:http://acm.nyist.net/JudgeOnline/problem.php?pid=37
思路1:dp动态规划
分析:d[i][j]表示从第 i 到第 j 的最优解;if(a[i]==a[j]) d[i][j]=d[i+1][j-1];d[i][j]=max{d[i][j] , d[i+1][j]+1 , d[i][j-1]+1};
代码如下:
1 #include<stdio.h> 2 #include<string.h> 3 #define N 1010 4 int d[N][N]; 5 char a[N]; 6 int min(int x,int y) 7 { 8 return x<y?x:y; 9 } 10 int main() 11 { 12 int i,j,p,n,test; 13 scanf("%d",&test); 14 while(test--) 15 { 16 scanf("%s",a+1); //这点让我贡献了好几个WA,至于为啥a+1,目前还不太清楚。。。 17 n=strlen(a+1); 18 for(i=1;i<=n;i++) 19 d[i][i]=d[i][i-1]=0; 20 for(p=1;p<n;p++) 21 { 22 for(i=1;i<=n-p;i++) 23 { 24 j=i+p; 25 d[i][j]=10000; 26 if(a[i]==a[j]) 27 d[i][j]=d[i+1][j-1]; 28 else 29 { 30 d[i][j]=min(d[i][j],d[i+1][j]+1); 31 d[i][j]=min(d[i][j],d[i][j-1]+1); 32 } 33 } 34 } 35 printf("%d\n",d[1][n]); 36 } 37 return 0; 38 } 39
思路2:在纸上测试几组数据,发现先逆转原来的字符串,再用原来的字符串跟逆转后的字符串进行比较,求得的最长公共子序列就是回文串,也就是不需要添加的,再用总长度减去最长公共子序列就可以得到最少需要添加的字符数。代码就简单了,以前写过的稍稍改下就直接贴上来了。
本题看起来似乎比较难,但其实如果想出思路就发现,其实可以转化为一个最长公共子序列问题,求出字符串及其反转的最长公共子序列长度,再把反转后的非公共部分填充进原字符串就行了,如下表:
原串: | 1 | 2 | 3 | 4 | 1 | ||
反转: | 1 | 4 | 3 | 2 | 1 | ||
结果: | 1 | 4 | 2 | 3 | 2 | 4 | 1 |
代码如下:
1 #include<stdio.h> 2 #include<string.h> 3 int f[1001][1001]; 4 int main() 5 { 6 char str1[1001],str2[1001]; 7 int ncases,i,j,k,len; 8 scanf("%d",&ncases); 9 while(ncases--) 10 { 11 memset(str1,0,sizeof(str1)); 12 memset(str2,0,sizeof(str2)); 13 scanf("%s",str1); 14 len=strlen(str1); 15 for(k=0,i=len-1;i>=0;i--)//**转置字符串**// 16 { 17 str2[k++]=str1[i]; 18 } 19 for(i=0;i<=len;i++) 20 { 21 f[i][0]=f[0][i]=0; 22 } 23 for(i=1;i<=len;i++) 24 { 25 for(j=1;j<=len;j++) 26 { 27 if(str1[i-1]==str2[j-1])//**求最长公共子序列**// 28 { 29 f[i][j]=f[i-1][j-1]+1; 30 } 31 else 32 { 33 f[i][j]=f[i-1][j]>f[i][j-1]?f[i-1][j]:f[i][j-1]; 34 } 35 } 36 } 37 printf("%d\n",len-f[len][len]);//**用长度减去最长公共子序列得到需要最少添加的字符数**// 38 } 39 return 0; 40 }