题目描述
难度分:1500
输入长度≤2000的字符串s,只包含小写英文字母。
从s中选两个不重叠的非空回文子串,有多少种选法?
输入样例1
aa
输出样例1
1
输入样例2
aaa
输出样例2
5
输入样例3
abacaba
输出样例3
36
算法
区间DP
感觉今天的茶和LeetCode每日一题重了,做法基本一样。
状态定义
ispalindrome[l][r]表示子串[l,r]是否是回文的。
状态转移
按照区间DP
的套路循环,外层从小到大枚举子串长度len,内层循环枚举子串的左端点l,那么子串的右端点就是r=l+len−1。如果s[l]=s[r],那[l,r]是否回文就取决于[l+1,r−1]是否回文。有状态转移方程ispalindrome[l][r]=ispalindrome[l+1][r−1]。
后缀和
接下来O(n2)枚举左边那个串[l,r],如果ispalindrome[l][r]=true
,只要知道r的右边有几个回文串就可以了。
假设suf[i]表示后缀[i,n]中回文串的个数,可以倒序遍历i,对每个i,先把[i+1,n]上面的回文串数量继承过来,即状态转移suf[i]=suf[i+1]。然后计算以s[i]开头的回文串有多少个,遍历右端点j≥i,只要ispalindrome[l][r]=true
,suf[i]就自增。
复杂度分析
时间复杂度
预处理出suf的时间复杂度为O(n2),即遍历左端点O(n),对一个固定的左端点,遍历可能的右端点O(n);区间DP
的时间复杂度也为O(n2),即枚举子串长度O(n),枚举子串左端点O(n)。因此,整个算法的时间复杂度为O(n2)。
空间复杂度
除了输入的字符串s,DP
矩阵ispalindrome的空间复杂度为O(n2),suf的空间复杂度为O(n)。因此,整个算法的额外空间复杂度为O(n2)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2010;
int n, suf[N];
bool ispalindrome[N][N];
char s[N];
int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
memset(ispalindrome, 0, sizeof(ispalindrome));
for(int i = 1; i <= n; i++) {
ispalindrome[i][i] = true;
suf[i] = 0;
if(i + 1 <= n) {
if(s[i] == s[i + 1]) ispalindrome[i][i + 1] = true;
}
}
for(int len = 3; len <= n; len++) {
for(int l = 1; l + len - 1 <= n; l++) {
int r = l + len - 1;
if(s[l] == s[r]) {
ispalindrome[l][r] = ispalindrome[l + 1][r - 1];
}
}
}
suf[n + 1] = 0;
for(int i = n; i >= 1; i--) {
suf[i] = suf[i + 1];
for(int j = i; j <= n; j++) {
if(ispalindrome[i][j]) {
suf[i]++;
}
}
}
long long ans = 0;
for(int i = 1; i < n; i++) {
for(int j = i; j < n; j++) {
if(ispalindrome[i][j]) {
ans += suf[j + 1];
}
}
}
printf("%lld\n", ans);
return 0;
}