题目大意:
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4632
给出
t
t
t个字符串,求每个字符串的回文子串个数。
思路:
这种题一看就是区间DP,只可惜想不到方程。
设
f
[
i
]
[
j
]
(
i
≤
j
)
f[i][j](i\leq j)
f[i][j](i≤j)表示
i
i
i到
j
j
j的区间的回文子串个数。
那么如何转移呢?
肯定是
[
i
,
k
]
[i,k]
[i,k]区间会问子串个数+
[
k
,
j
]
[k,j]
[k,j]区间回文子串个数+两区间交界处回文子串个数。
那么由于
k
k
k在
[
i
,
j
]
[i,j]
[i,j]的任意位置都不会影响答案(这道题很明显不会有多个解取最值),所以最好找到一个最容易计算答案的
k
k
k。
那么这个
k
k
k要么是
i
+
j
2
\frac{i+j}{2}
2i+j,要么肯定用容斥原理。
经过思考,后者更容易计算。根据容斥,很明显可以得到:
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])
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]
s[i]=s[j]呢?
那么中间的那一块(
f
[
i
+
1
]
[
j
−
1
]
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]−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]+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)
注意题目中说了取模。而且要注意负数取模!
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
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;
}