本题的这个状态转移方程真的是自己推出来的。。
用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。
下面贴代码:
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 }