字符子串和字符子序列的区别

字符字串指的是字符串中连续的n个字符;如palindrome中,pa,alind,drome等都属于它的字串

而字符子序列指的是字符串中不一定连续但先后顺序一致的n个字符;如palindrome中,plind,lime属于它的子序列,而mod,rope则不是,因为它们与字符串的字符顺序不一致。

Manacher's Algorithm

在计算机科学中,最长回文子串或最长对称因子问题是在一个字符串中查找一个最长的连续的回文的子串,例如“banana”最长回文子串是“anana”。最长回文子串并不一定是唯一的,比如“abracadabra”,没有超过3的回文子串,但是有两个回文字串长度都是3:“ada”和“aca”。在一些应用中,我们求出全部的极大回文子串(不被其他回文串包含的回文子串)。

Manacher于[1]发现了一种线性时间算法,可以在列出给定字符串中从任意位置开始的所有回文子串。并且,Apostolico, Breslauer & Galil [2]发现,同样的算法也可以在任意位置查找全部极大回文子串,并且时间复杂度是线性的。因此,他们提供了一种时间复杂度为线性的最长回文子串解法。另外,Jeuring (1994)[3], Gusfield (1997)[4]发现了基于后缀树的算法。也存在已知的高效并行算法。

最长回文子串算法不应当与最长回文子序列算法混淆。

要在线性时间内找出字符串的最长回文子串,这个算法必须利用回文和子回文的这些特点和观察

C++实现

constexpr auto MAXN = (int);

char s[MAXN << ], str[MAXN];
int RL[MAXN]; int Manacher(void) {
size_t len = strlen(str); *s = '#';
for (int i = ; i < len; i++) {
s[(i << ) + ] = str[i]; s[(i << ) + ] = '#';
}len = (len << ) + ; int max = , pos, maxRight = -; memset(RL, , sizeof(RL));
for (int i = ; i < len; i++) {
if (i < maxRight) RL[i] = std::min(RL[(pos << ) - i], maxRight - i);
else RL[i] = ;
while (i - RL[i] >= && i + RL[i] < len && s[i - RL[i]] == s[i + RL[i]])++RL[i];
if (i + RL[i] > maxRight) { pos = i; maxRight = i + RL[i]; }
max = std::max(max, RL[i] - );
} return max;
}

具体实现过程演示

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#define lson l,mid,idx<<1
#define rson mid+1,r,idx<<1|1
#define lc idx<<1
#define rc idx<<1|1
#define N 100010
#define ll long long using namespace std; char str[N],s[N];
int len[N]={};
int manachr(){
s[]='$';
int n=;
for(int i=;str[i];i++)s[n++]='#',s[n++]=str[i];
s[n++]='#';s[n]='\0';
int MAX=,id=,mix=;
for(int i=;i<n;i++){
if(i<mix)len[i]=min(len[*id-i],mix-i);
else len[i]=;
while(s[i-len[i]]==s[i+len[i]])len[i]++;
if(len[i]+i>mix)mix=len[i]+i,id=i,MAX=max(MAX,len[i]);
}
for(int i=;i<n;i++)printf("len[%d]=%d\n",i,len[i]);
return MAX-;
}
//mix 为id为中心 回文串最右端 当i<mix 时
// 利用回文串的性质 len[i]=len[id-(i-id)] 考虑到i会超过mix 和mix-i取最小
//
int main(int argc, char const *argv[])
{
cin >> str;
cout << manachr() << endl;
return ;
}

MATLAB实现

function m = Manacher(a)
T = ['$#' reshape([a;ones(size(a))*'#'], , '') '@'];
l = length(T);
P = zeros(, l); mx = ; %range
id = ; %center
for i = :l-
if i < mx
P(i) = min(P( * id - i), mx - i);
else
P(i) = ;
end while T(i+P(i)) == T(i-P(i))
P(i) = P(i) + ;
end if P(i) + i > mx
mx = P(i) + i;
id = i;
end
end
m = max(P)-;

最长回文子序列

分析

对任意字符串,如果头和尾相同,那么它的最长回文子序列一定是去头去尾之后的部分的最长回文子序列加上头和尾。如果头和尾不同,那么它的最长回文子序列是去头的部分的最长回文子序列和去尾的部分的最长回文子序列的较长的那一个。

str[0...n-1]是给定的字符串序列,长度为n,假设f(0,n-1)表示序列str[0...n-1]的最长回文子序列的长度。

1.如果str的最后一个元素和第一个元素是相同的,则有:f(0,n-1)=f(1,n-2)+2;例如字符串序列“AABACACBA”,第一个元素和最后一个元素相同,其中f(1,n-2)表示红色部分的最长回文子序列的长度;

2.如果str的最后一个元素和第一个元素是不相同的,则有:f(0,n-1)=max(f(1,n-1),f(0,n-2));例如字符串序列“ABACACB”,其中f(1,n-1)表示去掉第一元素的子序列,f(0,n-2)表示去掉最后一个元素的子序列。

设字符串为s,f(i,j)表示s[i..j]的最长回文子序列。

状态转移方程如下:

当i>j时,f(i,j)=0。

当i=j时,f(i,j)=1。

当i<j并且s[i]=s[j]时,f(i,j)=f(i+1,j-1)+2。

当i<j并且s[i]≠s[j]时,f(i,j)=max( f(i,j-1), f(i+1,j) )。

由于f(i,j)依赖i+1,所以循环计算的时候,第一维必须倒过来计算,从s.length()-1到0。

最后,s的最长回文子序列长度为f(0, s.length()-1)。

以"BBABCBCAB"为例:

(注:本程序的填表方向斜向左上,即每次从最后一行最后一个数开始,依次向左填写)

C++实现

int dp[][];
char str[N];
// dp[i][j] 代表 str[i]到str[j] 中回文子序列的最大长度
// str[i]==str[j] dp[i][j]=dp[i+1][j-1]+2;
// str[i]!=str[j] dp[i][j]=max(dp[i][j-1],dp[i+1][j]);
int main(){
cin>>(str+);
int n=strlen(str+);
for(int i=n;i>=;i--){
dp[i][i]=;
for(int j=i+;j<=n;j++){
if(str[i]==str[j])dp[i][j]=dp[i+][j-]+;
else dp[i][j]=max(dp[i+][j],dp[i][j-]);
}
}
cout<<dp[][n]<<endl;
}

为进一步减小空间复杂度,我们发现计算第i行时只用到了第i+1行,这样我们便不需要n行,只需要2行即可。

滚动数组优化

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int dp[][];
int main()
{
string str;
while(cin >> str)
{
int len = str.length(); memset(dp,,sizeof(dp));
int cur = ;
for(int i = len - ; i >= ; i--)
{
cur ^= ;
dp[cur][i] = ;
for(int j = i + ; j < len; j++)
{
if(str[i] == str[j])
dp[cur][j] = dp[cur^][j-] + ;
else
dp[cur][j] = max(dp[cur][j-],dp[cur^][j]);
}
}
cout<<dp[cur][len-]<<endl;
}
}

最长回文子序列/最长回文子串(DP,马拉车)的更多相关文章

  1. hdu6537 /// DP 最长不降子序列->最长公共子序列

    题目大意: 给定一个字符串 字符为0~9 求翻转某个区间后使得串中的最长不降子序列最长 因为字符范围为0~9 假设有一个 0 1 2 3 4 5 6 7 8 9 的序列 此时翻转某个区间得到形如 0 ...

  2. 简单动态规划——最长公共子序列&&最长回文子序列&&最长上升||下降子序列

    最长公共子序列,顾名思义当然是求两个字符串的最长公共子序列啦,当然,这只是一道非常菜的动规,所以直接附上代码: #include<iostream> #include<cstdio& ...

  3. NOIP2016提高组初赛(2)四、读程序写结果3、求最长回文子序列

    #include <iostream> using namespace std; int lps(string seq, int i, int j) { int len1, len2; i ...

  4. 最长回文子序列---DP

    问题描述 给定一个字符串s,找到其中最长的回文子序列.可以假设s的最大长度为1000. 解题思路 1.说明 首先要弄清楚回文子串和回文子序列的区别,如果一个字符串是"bbbab", ...

  5. HDU 4632 Palindrome subsequence(区间DP求回文子序列数)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4632 题目大意:给你若干个字符串,回答每个字符串有多少个回文子序列(可以不连续的子串).解题思路: 设 ...

  6. codevs 2185 最长公共上升子序列--nm的一维求法

    2185 最长公共上升子序列  时间限制: 1 s  空间限制: 32000 KB  题目等级 : 钻石 Diamond 题目描述 Description 熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目 ...

  7. 【24题】P2766最长不下降子序列问题

    网络流二十四题 网络流是个好东西,希望我也会. 网络流?\(orz\ zsy!!!!!\) P2766 最长不下降子序列问题 考虑我们是如何\(dp\)这个\(LIS\)的. 我们是倒着推,设置\(d ...

  8. JDOJ 1946 求最长不下降子序列个数

    Description 设有一个整数的序列:b1,b2,…,bn,对于下标i1<i2<…<im,若有bi1≤bi2≤…≤bim 则称存在一个长度为m的不下降序列. 现在有n个数,请你 ...

  9. [**P2766** 最长不下降子序列问题](https://www.luogu.org/problemnew/show/P2766)

    P2766 最长不下降子序列问题 考虑我们是如何\(dp\)这个\(LIS\)的. 我们是倒着推,设置\(dp(i)\)代表以\(i\)为起点的\(LIS\)是多少.转移太显然了 \[ dp(i)=m ...

随机推荐

  1. man da'te

    DATE(1)                 用户命令                      DATE(1) 名称  日期-打印或设置系统日期和时间  简介   date [OPTION]... ...

  2. sqlserver数据库脱机和分离的区别

    脱机和分离的区别: 分离和脱机都可以使数据库不能再被使用,但是分离后需要附加才能使用,而脱机后只需联机就可以用了. 附加数据库报错: 无法打开物理文件 XXX.mdf".操作系统错误 5:& ...

  3. TinyMCE不可编辑

    1. 通过配置在控件初始化时设置 tinyMCE.init({ readonly : 1 }); 2.tinymce.activeEditor.getBody().setAttribute('cont ...

  4. linux运维、架构之路-nfs网络文件系统

    一.nfs介绍  NFS是Network File System的缩写,是网络文件系统,它的主要功能是通过网络(一般是局域网)让不同的主机系统之间可以共享文件或目录,主要存储用户上传的图片附件等信息. ...

  5. 前端面试题常考&必考之--清除浮动的方法

    浮动 问题:子元素设置了float后,脱离父元素,导致父元素无法撑开?(也就是子元素的高度没有过渡到父元素) 例子: 检查元素的效果: (三种)常用的解决办法: 1>额外标签法,添加一个空的di ...

  6. C#与.NET Framework c#编程语言,和java是一样的。(c#,java) -->javaweb,asp.net

  7. JavaScript性能优化之小知识总结

    1.避免全局查找 在一个函数中会用到全局对象存储为局部变量来减少全局查找,因为访问局部变量的速度要比访问全局变量的速度更快些 2.定时器 如果针对的是不断运行的代码,不应该使用setTimeout,而 ...

  8. Java——容器(Interator)

    [Interator接口]   <1> 所有实现了Collection接口的容器类都有一个interator方法用以返回一个实现了Interaor接口的对象. <2> Inte ...

  9. jq和js用法:入口写法

    jq和js入口写法demo: <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  10. java操作JSON字符串转换成对象的时候如何可以不建立实体类也能获取数据

    引入依赖 <dependency>    <groupId>com.alibaba</groupId>    <artifactId>fastjson& ...