题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4632
【题目描述】
《回文子串数量》
给你一个长度为N(N≤1000)的字符串,你输出它所有的回文子串的数量(对10007取模)。
只要从字符串 s 中顺序地取出一些字符(不要求连续,可以是 s 本身,不能是空串),不改变他们的顺序的情况下,这个子串是一个回文串,那么他就是一个 s 的回文子串。
【输入格式】
首先是一个数T(T≤50),表示测试用例的数量。
接下来T行,每行包含一个字符串。
【输出格式】
对于每一个字符串,你需要输出它的回文子串数量(由于数据量可能会比较大,所以结果需要对10007取模)。
【样例输入】
4
a
aaaaa
goodafternooneveryone
welcometoooxxourproblems
【样例输出】
Case 1: 1
Case 2: 31
Case 3: 421
Case 4: 960
【题目分析】
涉及的知识点:区间动态规划(区间DP)。
首先我们假设 dp[i][j] 表示字符串 s 的子串 s[i..j] 中包含的回文子串的数量。
我们可以得到:
· 当i<j且s[i]!=s[j]时, dp[i][j] = dp[i][j-1] ∪ dp[i+1][j] (这里“∪”符号表示)两个集合的并,
根据容斥原理,我们可以得到:
  dp[i][j] = dp[i][j-1] ∪ dp[i+1][j] = dp[i][j-1] + dp[i+1][j] - dp[i+1][j-1]
· 当i<j且s[i]==s[j]时,dp[i][j]的构成除了上述部分以外,还有 s[i] + s' + s[j] 部分,其中 s' 表示 s[i+1..j-1] 中的任意一个回文串 加上一个特殊的空字符串
所以此时:
  dp[i][j] = dp[i][j-1] ∪ dp[i+1][j] + dp[i+1][j-1] + 1 = dp[i][j-1] + dp[i+1][j] + 1
假设字符串的长度为n,坐标从0开始,那么我们最终的答案就是 dp[0][n-1]。
需要注意的一些细节:
因为这里在运算的时候可能会出现负数,所以比较好的解决办法是加上一个 MOD 在进行取模操作,就不会出现负数了。
据此,我们可以编写代码如下:

 

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
#define MOD 10007
const int maxn = 1001;
int T, n, dp[maxn][maxn];
string s;

int main() {
    cin >> T;
    for (int cas = 1; cas <= T; cas ++) {
        cin >> s;
        n = s.length();
        fill(dp[0], dp[0]+maxn*maxn, 0);
        for (int l = 1; l <= n; l ++) {
            for (int i = 0; i+l-1 < n; i ++) {
                int j = i + l - 1;
                if (l == 1) dp[i][j] = 1;
                else if (s[i] == s[j]) dp[i][j] = (dp[i][j-1] + dp[i+1][j] + 1) % MOD;
                else {
                    dp[i][j] = (dp[i][j-1] + dp[i+1][j] + MOD - (l > 2 ? dp[i+1][j-1] : 0)) % MOD;
                }
            }
        }
        cout << "Case " << cas << ": " << dp[0][n-1] << endl;
    }
    return 0;
}

  

posted on 2019-06-07 23:13  月亮编程  阅读(201)  评论(0编辑  收藏  举报