金字塔

利用dfs序的通性,如果[l, r]是一颗完整的子树,一定有str[l] = str[r],
接下来就是考虑如何划分子树了。

考虑对[l, r]的子树进行划分,枚举中间划分值mid,那么因为dfs序一定有str[l] = str[mid] = str[r]。
我们定义dp[l][r],为区间[l][r],也就是以l,为根节点的划分方案数,
当有mid满足条件时,一定有dp[l][r] = dp[l][r] + dp[l + 1][mid - 1] * dp[mid][r]
dp[l + 1][mid - 1]为单独作为子树的方案,
dp[mid][r]这棵树没有加上[l + 1][r - 1]区间的方案树。
然后再特判一个ABA形式的情况,也就是两个节点的树的特殊情况。
之后再进行区间dp转移即可。

/*
  Author : lifehappy
*/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 310, mod = 1e9;

int dp[N][N], n;

char str[N];

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    scanf("%s", str + 1);
    n = strlen(str + 1);
    for(int i = 1; i <= n; i++) {
        dp[i][i] = 1;
    }
    for(int len = 3; len <= n; len++) {
        for(int l = 1; l + len - 1 <= n; l++) {
            int r = l + len - 1;
            if(str[l] != str[r]) continue;
            dp[l][r] = dp[l + 1][r - 1];
            for(int mid = l + 2; mid <= r - 2; mid++) {
                if(str[l] == str[mid]) {
                    dp[l][r] = (dp[l][r] + 1ll * dp[l + 1][mid - 1] * dp[mid][r] % mod) % mod;
                }
            }
        }
    }
    printf("%lld\n", dp[1][n]);
    return 0;
}