UVA 10617 Again Palindrome

本题的这个状态转移方程真的是自己推出来的。。

用dp(i,j)表示从第i个字符到第j个字符删去字符使得字符串为回文串的方法数,得到的状态转移方程是:

if(str[i]==str[j]) dp(i,j)=dp(i,j-1)+dp(i+1,j)+1

else     dp(i,j)=dp(i,j-1)+dp(i+1,j)-dp(i+1,j-1)

其中dp数组要用long long int,否则就会像我一样TLE。。

下面简单讲一下我自己的思路。

先讲if(str[i]!=str[j]) dp(i,j)=dp(i,j-1)+dp(i+1,j)-dp(i+1,j-1)

当第i个字符跟第j个字符不相等时,首先,去掉第j个字符,考虑第i个字母到第j-1个字母所构成的字符串,得到使得他们构成回文串的方法数。这显然是dp(i,j)的一部分。

同理,去掉第i个字符,考虑第i+1个字母到第j个字母所构成的字符串,得到使得他们构成回文串的方法数。这显然也是dp(i,j)的一部分。

但是,上述的两部分是有重复的,重复的部分就是同时去掉第i个和第j个字符时,第i+1个字母到第j+1个字母所构成的字符串的那部分dp(i+1,j-1)了。

所以,if(str[i]!=str[j]) dp(i,j)=dp(i,j-1)+dp(i+1,j)-dp(i+1,j-1)

 

再说说if(str[i]==str[j]) dp(i,j)=dp(i,j-1)+dp(i+1,j)+1

这个跟上面的思路基本一样,无非是第i个字符跟第j个字符相等,这样就会多出:以第i个字符为首,第j个字母为尾的,中间第i+1~第j-1个字符任意组合,使得其变成回文串的那些方法了,当然,这里还要再+1,因为中间的字符串可以使空的(此时这个回文串就是(str[i][str[j]))。

因此,当第i个字符跟第j个字符相等时,多出的方法就会有dp(i+1,j-1)+1种。

综上, dp(i,j)=[ dp(i,j-1)+dp(i+1,j)-dp(i+1,j-1) ]+dp(i+1,j-1)+1=dp(i,j-1)+dp(i+1,j)+1。

下面贴代码:

View Code
 1 #include <stdio.h>
 2 #include <string.h>
 3 long long int d[65][65];
 4 char str[65];
 5 long long int dp(int x,int y)
 6 {
 7     long long int &ans = d[x][y];
 8     if(ans != -1)
 9         return ans;
10     if(x > y)
11     {
12         ans = 0;
13         return ans;
14     }
15     if(x == y)
16     {
17         ans = 1;
18         return ans;
19     }
20     if(str[x] == str[y])
21         ans = dp(x,y - 1) + dp(x + 1,y) + 1;
22     else
23         ans = dp(x,y - 1) + dp(x + 1,y) - dp(x + 1,y - 1);
24     return ans;
25 }
26 int main()
27 {
28     int ncase,len;
29     scanf("%d",&ncase);
30     while(ncase--)
31     {
32         memset(d,-1,sizeof(d));
33         scanf("%s",str);
34         len = strlen(str);
35         printf("%lld\n",dp(0,len - 1));
36     }
37     return 0;
38 }
原文地址:https://www.cnblogs.com/zhexipinnong/p/2502176.html