1. 程式人生 > >【HDU4632】Palindrome subsequence【區間DP】

【HDU4632】Palindrome subsequence【區間DP】

題目大意:

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=4632
給出 t t 個字串,求每個字串的迴文子串個數。


思路:

這種題一看就是區間DP,只可惜想不到方程。
f [ i

] [ j ] ( i j ) f[i][j](i\leq j)
表示 i i j j 的區間的迴文子串個數。
那麼如何轉移呢?
肯定是 [ i
, k ] [i,k]
區間會問子串個數+ [ k , j ] [k,j] 區間迴文子串個數+兩區間交界處迴文子串個數。
那麼由於 k k [ i , j ] [i,j] 的任意位置都不會影響答案(這道題很明顯不會有多個解取最值),所以最好找到一個最容易計算答案的 k k
那麼這個 k k 要麼是 i + j 2 \frac{i+j}{2} ,要麼肯定用容斥原理。
經過思考,後者更容易計算。根據容斥,很明顯可以得到:
f [ i ] [ j ] = f [ i + 1 ] [ j ] + f [ i ] [ j 1 ] f [ i + 1 ] [ j 1 ] ( s [ i ] ! = s [ j ] ) f[i][j]=f[i+1][j]+f[i][j-1]-f[i+1][j-1](s[i]!=s[j])
那麼如果 s [ i ] = s [ j ] s[i]=s[j] 呢?
那麼中間的那一塊( f [ i + 1 ] [ j 1 ] f[i+1][j-1] )中的每一個迴文子串都會受到影響。所以就有:
f [ i ] [ j ] = f [ i + 1 ] [ j ] + f [ i ] [ j 1 ] f [ i + 1 ] [ j 1 ] + f [ i + 1 ] [ j 1 ] + 1 f[i][j]=f[i+1][j]+f[i][j-1]-f[i+1][j-1]+f[i+1][j-1]+1

f [ i ] [ j ] = f [ i + 1 ] [ j ] + f [ i ] [ j 1 ] + 1 f[i][j]=f[i+1][j]+f[i][j-1]+1
那麼也可以將兩個方程合二為一:
f [ i ] [ j ] = f [ i + 1 ] [ j ] + f [ i ] [ j 1 ] f [ i + 1 ] [ j 1 ] + ( s [ i 1 ] = = s [ j 1 ] ? f [ i + 1 ] [ j 1 ] + 1 : 0 ) f[i][j]=f[i+1][j]+f[i][j-1]-f[i+1][j-1]+(s[i-1]==s[j-1]?f[i+1][j-1]+1:0)
注意題目中說了取模。而且要注意負數取模!
f [ i ] [ j ] = ( ( f [ i + 1 ] [ j ] + f [ i ] [ j 1 ] f [ i + 1 ] [ j 1 ] + ( s [ i 1 ] = = s [ j 1 ] ? f [ i + 1 ] [ j 1 ] + 1 : 0 ) ) % M O D + M O D ) % M O D f[i][j]=((f[i+1][j]+f[i][j-1]-f[i+1][j-1]+(s[i-1]==s[j-1]?f[i+1][j-1]+1:0))\%MOD+MOD)\%MOD


程式碼:

#include <cstdio>
#include <cstring>
#include <iostream>
#define N 1010
#define MOD 10007
using namespace std;

int f[N][N],t,len;
char s[N];

int main()
{
	scanf("%d",&t);
	for (int l=1;l<=t;l++)
	{
		cin>>s;
		for (int i=1;i<=strlen(s);i++)
		 f[i][i]=1;  //每個長度為1的子串有1個迴文串(它本身)
		for (int i=strlen(s)-1;i>0;i--)  //左端點
		 for (int j=i+1;j<=strlen(s);j++)  //右端點
		  f[i][j]=((f[i+1][j]+f[i][j-1]-f[i+1][j-1]+(s[i-1]==s[j-1]?f[i+1][j-1]+1:0))%MOD+MOD)%MOD;
		printf("Case %d: %d\n",l,f[1][strlen(s)]%MOD);
	}
	return 0;
}