HDU 4632 Palindrome subsequence & FJUT3681 回文子序列种类数(回文子序列个数/回文子序列种数 容斥 + 区间DP)题解

题意1:问你一个串有几个不连续子序列(相同字母不同位置视为两个)

题意2:问你一个串有几种不连续子序列(相同字母不同位置视为一个,空串视为一个子序列)

思路1:由容斥可知当两个边界字母相同时 dp[i][j] = dp[i + 1][j] + dp[i][j - 1] - dp[i + 1][j - 1] + dp[i + 1][j - 1] + 1;当两个字母不同时 dp[i][j] = dp[i + 1][j] + dp[i][j - 1] - dp[i + 1][j - 1]。然后区间DP即可

思路2:由思路1我们能大致知道怎么做,显然两边界字母不一样时情况是一样的。当两边字母一样时,那么就要判断中间的重复情况。

我们设l和r,表示i + 1 ~ j - 1里最左边的s[i]字母和最右边的s[i]字母

当 l == r 那么就只有一个相同字母,dp[i][j] = dp[i + 1][j - 1] + dp[i + 1][j - 1] + 1,答案为中间部分 + 中间加上两边界 + s[i]s[j]串

当 l > r,没有这个字母,dp[i][j] = dp[i + 1][j - 1] + dp[i + 1][j - 1] + 2,答案为中间部分 + 中间加上两边界 + s[i]s[j]串 + s[i]

当l < r,说明至少有两个字母,dp[i][j] = dp[i + 1][j - 1] + dp[i + 1][j - 1] - dp[l + 1][r - 1],答案为中间部分 + 中间加上两边界 - (l,r)区间内种数,因为这里面的和s[l],s[r]组成的串和s[i],s[j]重复

代码1:

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<sstream>
#include<iostream>
#include<algorithm>
typedef long long ll;
using namespace std;
const int maxn = 1e3 + 10;
const ll MOD = 1e4 + 7;
const int INF = 0x3f3f3f3f;
int dp[maxn][maxn]; //i到j种数
char s[maxn];
int main(){
    int t, ca = 1;
    scanf("%d", &t);
    while(t--){
        scanf("%s", s + 1);
        int n = strlen(s + 1);
        for(int i = 1; i <= n; i++){
            dp[i][i] = 1;
        }
        for(int len = 2; len <= n; len++){
            for(int i = 1; i + len - 1 <= n; i++){
                int j = i + len - 1;
                if(s[i] == s[j]){
                    dp[i][j] = dp[i + 1][j] + dp[i][j - 1] + 1;
                }
                else{
                    dp[i][j] = dp[i + 1][j] + dp[i][j - 1] - dp[i + 1][j - 1];
                }
                dp[i][j] = (dp[i][j] + MOD) % MOD;
            }
        }
        printf("Case %d: %d\n", ca++, dp[1][n]);
    }
    return 0;
}

 

代码2:

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<sstream>
#include<iostream>
#include<algorithm>
typedef long long ll;
using namespace std;
const int maxn = 1e3 + 10;
const ll MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
ll dp[maxn][maxn]; //i到j种数
char s[maxn];
int main(){
    int t, ca = 1, n;
    scanf("%d", &t);
    while(t--){
        scanf("%s", s + 1);
        int n = strlen(s + 1);
        for(int i = 1; i <= n; i++){
            dp[i][i] = 1;
        }
        for(int len = 2; len <= n; len++){
            for(int i = 1; i + len - 1 <= n; i++){
                int j = i + len - 1;
                if(s[i] == s[j]){
                    int l = i + 1, r = j - 1;
                    while(s[l] != s[i] && l <= r) l++;
                    while(s[r] != s[i] && l <= r) r--;
                    if(l > r){
                        dp[i][j] = dp[i + 1][j - 1] + dp[i + 1][j - 1] + 2;
                    }
                    else if(l == r){
                        dp[i][j] = dp[i + 1][j - 1] + dp[i + 1][j - 1] + 1;
                    }
                    else{
                        dp[i][j] = dp[i + 1][j - 1] + dp[i + 1][j - 1] - dp[l + 1][r - 1];
                    }
                }
                else{
                    dp[i][j] = dp[i + 1][j] + dp[i][j - 1] - dp[i + 1][j - 1];
                }
            }
        }
        printf("Case %d: %lld\n", ca++, dp[1][n]);
    }
    return 0;
}

 

posted @ 2019-03-10 16:15  KirinSB  阅读(212)  评论(0编辑  收藏  举报