回文子序列有关的DP
最长回文子序列
最长回文子序列(https://leetcode-cn.com/problems/longest-palindromic-subsequence/)
题目描述
最长回文子序列
给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000 。
示例 1:
输入:
"bbbab"
输出:
4
一个可能的最长回文子序列为 "bbbb"。
示例 2:
输入:
"cbbd"
输出:
2
一个可能的最长回文子序列为 "bb"。
提示:
1 <= s.length <= 1000
s 只包含小写英文字母
思路:
用小区间推大区间
class Solution {
public:
int f[1005][1005];
int longestPalindromeSubseq(string s) {
int n=s.size();
for(int i=0; i<n; i++){
f[i][i]=1;
f[i][i+1]=1;
if(i+1<n&&s[i]==s[i+1]){
f[i][i+1]=2;
}
}
for(int len=3; len<=n; len++){
for(int L=0; L<=n-len; L++){
int R=L+len-1;
f[L][R]=max(f[L+1][R], f[L][R-1]);
if(s[L]==s[R]){
f[L][R]=f[L+1][R-1]+2;
}
}
}
return f[0][n-1];
}
}T;
回文子序列计数
https://nanti.jisuanke.com/t/10096
题目
如果从字符串 A 中选出一些字符,在不改变这些字符的顺序的情况下得到字符串 B,那么字符串 B 被称为字符串 A 的子序列。如果该子序列的逆序序列与元字符串完全相同,那么该子序列被称为回文子序列。已知一个字符串 S,求该字符串的所有回文子序列的数量,如果两个子序列在原字符串中的位置不同,字符内容相同,它们也是不同的回文子序列。
输入格式
第一行包含一个整数 Ca,表示有 Ca 组测试数据,对于每组测试数据:
输入包含一行,该行包含一个字符串 S,字符串 S 的长度不超过 1000,并且只包含小写英文字母。
输出格式
对于每组测试数据,输出 Case c: ans,其中 c 为测试数据编号,ans 为回文子序列的数量。
样例输入
3
aaa
a
aba
样例输出
Case 1: 7
Case 2: 1
Case 3: 5
思路
动态规划思想
对于任意字符串,如果头尾字符不相等,则字符串的回文子序列个数就等于去掉头的字符串的回文子序列个数+去掉尾的字符串的回文子序列个数-去掉头尾的字符串的回文子序列个数;如果头尾字符相等,那么除了上述的子序列个数之外,还要加上首尾相等时新增的子序列个数,1+去掉头尾的字符串的回文子序列个数,1指的是加上头尾组成的回文子序列,如aa,bb等。
因此动态规划的状态转移方程为:
设字符串为str,长度为n,f[i][j]表示第i到第j个字符间的回文子序列的个数(i<=j),则:
状态初始条件:dp[i][i]=1 (i=0:n-1)
状态转移方程:
dp[i][j]=dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1] if(str[i]!=str[j])
dp[i][j]=dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1]+dp[i+1][j-1]+1 if(str[i]==str[j])
#include <bits/stdc++.h>
#define LL long long
using namespace std;
char s[1005];
LL f[1005][1005];
const int mod=10007;
int main(){
int T, cas=1; scanf("%d", &T);
while(T--){
scanf("%s", s+1);
int n=strlen(s+1);
for(int i=1; i<=n; i++){
f[i][i]=1; f[i][i+1]=2;
if(s[i]==s[i+1]){
f[i][i+1]=3;
}
}
for(int Len=3; Len<=n; Len++){
for(int L=1; L<=n-Len+1; L++){
int R=L+Len-1;
if(s[L]!=s[R]){
f[L][R]=(f[L+1][R]+f[L][R-1]-f[L+1][R-1]+mod)%mod;
}
else{
f[L][R]=(f[L+1][R]+f[L][R-1]+1)%mod;
}
}
}
printf("Case %d: %lld\n", cas++, f[1][n]);
}
return 0;
}
每个i为中心的回文子序列计数
https://ac.nowcoder.com/acm/problem/21587
题目描述
我们称正着读与反着读一样的串为回文串,比如abba与racecar都是回文串
我们称长度为奇数的回文为奇回文,中间的字符称为回文中心
有一个字符串S包含N个小写字母
令x[i]表示以i为回文中心的回文子序列的个数 (0 <= i <= N-1)
换句话说:保留第i个字符,X[i]表示有多少种字符的删除方案可以使得剩下的字符是一个以i为中心的回文串
Y[i] = ((i+1) * X[i])%1000000007
返回所有Y的xor之和
所有字母都是小写字母
输入描述:
输入一行包含一个字符串,字符串的长度(1 <= N <= 3000)
输出描述:
输出一个整数
示例1
输入
复制
axbcba
输出
复制
31
示例2
输入
复制
eeeee
输出
复制
14
示例3
输入
复制
zyzyzzzzxzyz
输出
复制
221
示例4
输入
复制
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
输出
复制
1044407608
备注:
子任务1:n<=100
子任务2:n<=500
子任务3:n<=3000
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e3+5;
const int mod=1e9+7;
ll X[N],dp[N];//dp[i]代表以i为结尾的回文子序列数目
char s[N];
int main(){
scanf("%s",s+1);
int len=strlen(s+1);
X[1]=1;
X[len]=1;
for(int i=2;i<len;i++){
X[i]=1;
ll sum=0,cnt=0;
for(int j=len;j>i;j--){
cnt=dp[j];
if(s[j]==s[i-1]){
dp[j]=(dp[j]+sum+1)%mod;
}
sum=(sum+cnt)%mod;
X[i]=(X[i]+dp[j])%mod;
}
}
ll ans=0;
for(int i=1;i<=len;i++) ans=ans^(((i)*X[i])%mod);
printf("%lld\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)