最长连续回文串(最优线性时间O(n))
转自:http://blog.csdn.net/hopeztm/article/details/7932245
Given a string S, find the longest palindromic substring in S.
给出一个字符串S,找到一个最长的连续回文串。
例如串 babcbabcbaccba 最长回文是:abcbabcba
这个题目小弟给出3中解法,前两种的都是 O(n^2), 第三种思路是O(n).
思路1. 动态规划
这里动态规划的思路是 dp[i][j] 表示的是 从i 到 j 的字串,是否是回文串。
则根据回文的规则我们可以知道:
如果s[i] == s[j] 那么是否是回文决定于 dp[i+1][ j - 1]
当 s[i] != s[j] 的时候, dp[i][j] 直接就是 false。
动态规划的进行是按照字符串的长度从1 到 n推进的。
代码很明晰:给出java代码,复杂度 O(n^2)
public class DPSolution { boolean[][] dp; public String longestPalindrome(String s)
{
if(s.length() == 0)
{
return "";
}
if(s.length() == 1)
{
return s;
} dp = new boolean[s.length()][s.length()]; int i,j; for( i = 0; i < s.length(); i++)
{
for( j = 0; j < s.length(); j++)
{
if(i >= j)
{
dp[i][j] = true; //当i == j 的时候,只有一个字符的字符串; 当 i > j 认为是空串,也是回文 }
else
{
dp[i][j] = false; //其他情况都初始化成不是回文
}
}
} int k;
int maxLen = 1;
int rf = 0, rt = 0;
for( k = 1; k < s.length(); k++)
{
for( i = 0; k + i < s.length(); i++)
{
j = i + k;
if(s.charAt(i) != s.charAt(j)) //对字符串 s[i....j] 如果 s[i] != s[j] 那么不是回文
{
dp[i][j] = false;
}
else //如果s[i] == s[j] 回文性质由 s[i+1][j-1] 决定
{
dp[i][j] = dp[i+1][j-1];
if(dp[i][j])
{
if(k + 1 > maxLen)
{
maxLen = k + 1;
rf = i;
rt = j;
}
}
}
}
}
return s.substring(rf, rt+1);
}
}
思路2. KMP匹配
第二个思路来源于字符串匹配,最长回文串有如下性质:
对于串S, 假设它的 Reverse是 S', 那么S的最长回文串是 S 和 S' 的最长公共字串。
例如 S = abcddca, S' = acddcba, S和S'的最长公共字串是 cddc 也是S的最长回文字串。(参见本博客http://www.cnblogs.com/xubenben/p/3330712.html可获得线性时间算法)
如果S‘是 模式串,我们可以对S’的所有后缀枚举(S0, S1, S2, Sn) 然后用每个后缀和S匹配,寻找最长的匹配前缀。
例如当前枚举是 S0 = acddcba 最长匹配前缀是 a
S1 = cddcba 最长匹配前缀是 cddc
S2 = ddcba 最长匹配前缀是 ddc
当然这个过程可以做适当剪枝,如果当前枚举的后缀长度,小于当前找到的最长匹配,则直接跳过。
Java 代码如下:
public class Solution {
private int[] next;
private void GetNext(String s) //KMP求next数组
{
int i,j; i = 0;
j = -1; next[0] = -1; while( i < s.length())
{
if( j == -1 || s.charAt(i) == s.charAt(j))
{
i++;
j++;
next[i] = j;
}
else
{
j = next[j];
}
}
}
private int compare(String pattern, String s) //用KMP算法做求出最长的前缀匹配
{
int i,j; i = 0;
j = 0; int maxLen = 0;
while( i < s.length())
{
if(j == -1 || pattern.charAt(j) == s.charAt(i))
{
i++;
j++;
}
else
{
j = next[j];
}
if( j > maxLen)
{
maxLen = j;
}
if(j == pattern.length())
{
return maxLen;
}
}
return maxLen;
} public String longestPalindrome(String s) //
{
// Start typing your Java solution below
// DO NOT write main() function
String reverString = new StringBuilder(s).reverse().toString(); //求得到 输入string 的reverse
next = new int[s.length() + 1]; String maxPal = "";
int maxLen = 0;
int len;
for(int i = 0; i < s.length(); i++) //枚举所有后缀
{
String suffix = reverString.substring(i);
if(suffix.length() < maxLen)
{
break;
}
GetNext(suffix);
len = compare(suffix, s);
if( len > maxLen)
{
maxPal = suffix.substring(0, len);
maxLen = len;
} }
return maxPal; }
}
思路3. 思路来源于此
http://www.leetcode.com/2011/11/longest-palindromic-substring-part-ii.html
不过原文的陈述仔细研究了一下,有一些地方让人着实费解,所以自己决定重写一遍。
这里描述了一个叫Manacher’s Algorithm的算法。
算法首先将输入字符串S, 转换成一个特殊字符串T,转换的原则就是将S的开头结尾以及每两个相邻的字符之间加入一个特殊的字符,例如#
例如: S = “abaaba”, T = “#a#b#a#a#b#a#”.
为了找到最长的回文字串,例如我们当前考虑以Ti为回文串中间的元素,如果要找到最长回文字串,我们要从当前的Ti扩展使得 Ti-d … Ti+d 组成最长回文字串. 这里 d 其实和 以Ti为中心的回文串长度是一样的. 进一步解释就是说,因为我们这里插入了 # 符号,对于一个长度为偶数的回文串,他应该是以#做为中心的,然后向两边扩,对于长度是奇数的回文串,它应该是以一个普通字符作为中心的。通过使用#,我们将无论是奇数还是偶数的回文串,都变成了一个以Ti为中心,d为半径两个方向扩展的问题。并且d就是回文串的长度。
例如 #a#b#a#, P = 0103010, 对于b而言P的值是3,是最左边的#,也是延伸的最左边。这个值和当前的回文串是一致的。
如果我们求出所有的P值,那么显然我们要的回文串,就是以最大P值为中心的回文串。
T = # a # b # a # a # b # a #
P = 0 1 0 3 0 1 6 1 0 3 0 1 0
例如上面的例子,最长回文是 “abaaba”, P6 = 6.
根据观察发现,如果我们在一个位置例如 abaaba的中间位置,用一个竖线分开,两侧的P值是对称的。当然这个性质不是在任何时候都会成立,接下来就是分析如何利用这个性质,使得我们可以少算很多P的值。
下面的例子 S = “babcbabcbaccba” 存在更多的折叠回文字串。
Now we are at index i = 15, and its mirrored index around C is i’ = 7. Is P[ 15 ] = P[ 7 ] = 7?
当时当i = 15的时候,却只能得到回文 “a#b#c#b#a”, 长度是5, 而对称 i ' = 7 的长度是7.
如上图所示,如果以 i, i' 为中心,画出对称的区域如图,其中以i‘ = 7 对称的区域是 实心绿色 + 虚绿色 和 左侧,虚绿色表示当前的对称长度已经超过之前的对称中心C。而之前的P对称性质成立的原因是 i 右侧剩余的长度 R - i 正好比 以 i‘ 为中心的回文小。
then P[ i ] ← P[ i' ]
else P[ i ] ≥R – i. (这里下一步操作是扩充 P[ i ].
扩充P[i] 之后,我们还要做一件事情是更新 R 和 C, 如果当前对称中心的最右延伸大于R,我们就更新C和R。在迭代的过程中,我们试探i的时候,如果P[i'] <= R - i, 那么只要做一件事情。 如果不成立我们对当前P[i] 做扩展,因为最大长度是n,扩展最多就做n次,所以最多做2*n。 所以最后算法复杂度是 O(n)
或许贴上代码更容易一些。直接使用大神的代码了,虽然自己也实现了,不过是理解大神的思路实现的。
// Transform S into T.
// For example, S = "abba", T = "^#a#b#b#a#$".
// ^ and $ signs are sentinels appended to each end to avoid bounds checking
string preProcess(string s) {
int n = s.length();
if (n == 0) return "^$";
string ret = "^";
for (int i = 0; i < n; i++)
ret += "#" + s.substr(i, 1); ret += "#$";
return ret;
} string longestPalindrome(string s) {
string T = preProcess(s);
int n = T.length();
int *P = new int[n];
int C = 0, R = 0;
for (int i = 1; i < n-1; i++) {
int i_mirror = 2*C-i; // equals to i' = C - (i-C) P[i] = (R > i) ? min(R-i, P[i_mirror]) : 0; // Attempt to expand palindrome centered at i
while (T[i + 1 + P[i]] == T[i - 1 - P[i]])
P[i]++; // If palindrome centered at i expand past R,
// adjust center based on expanded palindrome.
if (i + P[i] > R) {
C = i;
R = i + P[i];
}
} // Find the maximum element in P.
int maxLen = 0;
int centerIndex = 0;
for (int i = 1; i < n-1; i++) {
if (P[i] > maxLen) {
maxLen = P[i];
centerIndex = i;
}
}
delete[] P; return s.substr((centerIndex - 1 - maxLen)/2, maxLen);
}
最长连续回文串(最优线性时间O(n))的更多相关文章
- 马拉车 o(n)(最长连续回文串) hdu 3068
#include<bits/stdc++.h> ; using namespace std; +]; string manacher(string ss) { string tt=&quo ...
- 算法 -- 四种方法获取的最长“回文串”,并对时间复杂进行分析对比&PHP
https://blog.csdn.net/hongyuancao/article/details/82962382 “回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就 ...
- bzoj2565: 最长双回文串 pam
题意:找一个串中的最长连续两个回文子串长度 题解:建两个回文树,一个正着,一个反着,每次add之后last的长度就是后缀最长的回文串长度,然后两边加一遍即可 /******************** ...
- Manacher(最长递减回文串)
http://acm.hdu.edu.cn/showproblem.php?pid=4513 Problem Description 吉哥又想出了一个新的完美队形游戏! 假设有n个人按顺序站在他的面前 ...
- 【BZOJ】【2565】最长双回文串
Manacher算法 找出一个最长子串S=X+Y,且X和Y都是回文串,求最长的长度是多少…… 同时找两个串明显很难搞啊……但是我们可以先找到所有的回文串!在找回文串的同时我们可以预处理出来l[i]和r ...
- BZOJ 2565: 最长双回文串 [Manacher]
2565: 最长双回文串 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1842 Solved: 935[Submit][Status][Discu ...
- 【BZOJ2565】最长双回文串(回文树)
[BZOJ2565]最长双回文串(回文树) 题面 BZOJ 题解 枚举断点\(i\) 显然的,我们要求的就是以\(i\)结尾的最长回文后缀的长度 再加上以\(i+1\)开头的最长回文前缀的长度 至于最 ...
- 最长(大)回文串的查找(字符串中找出最长的回文串)PHP实现
首先还是先解释一下什么是回文串:就是从左到右或者从右到左读,都是同样的字符串.比如:上海自来水来自海上,bob等等. 那么什么又是找出最长回文串呢? 例如:字符串abcdefedcfggggggfc, ...
- BZOJ.2565.[国家集训队]最长双回文串(Manacher/回文树)
BZOJ 洛谷 求给定串的最长双回文串. \(n\leq10^5\). Manacher: 记\(R_i\)表示以\(i\)位置为结尾的最长回文串长度,\(L_i\)表示以\(i\)开头的最长回文串长 ...
随机推荐
- JQuery识别键盘操作 & 键盘快捷键
前几天写的那个项目登陆页是直接点击but登陆的,后来做完了之后不断的测试的时候就发现蛋疼之处了 每次在键盘上输入一长串密码之后,还得抬起手拿鼠标点一下确认登陆 直接就搜了一下,看了一下书 = = 其实 ...
- COUNT(*),count(1),COUNT(ALL expression),COUNT(DISTINCT expression) BY Group by
select column_2,count(column_2) as 'count(column_2)' ,count(column_1) as 'count(column_1)' ,count(*) ...
- [转]C++编写Config类读取配置文件
//Config.h #pragma once #include <string> #include <map> #include <iostream> #incl ...
- Linux ls -l内容详解
ls -l是列出当前目录下所有文件信息 以下是实例: 具体的文字描述如下: 第1字段: 文件属性字段文件属性字段总共有10个字母组成,第一个字母表示文件类型,如果这个字母是一个减号”-”,则说明该文 ...
- word超链接显示HYPERLINK
在word中编辑超链接后显示的并不是正常的超链接 正常的超连接 非正常显示 解决办法: 文件---选项----高级,如下图 将“显示域代码而非值域”前面的勾去掉.
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
XMPP协议简介 XMPP协议(Extensible Messaging and PresenceProtocol,可扩展消息处理现场协议)是一种基于XML的协议,目的是为了解决及时通信标准而提出来的 ...
- JavaScript构建(编绎)系统大比拼:Grunt vs. Gulp vs. NPM
Nicolas Bevacqua进行了一个比较JavaScript构建(编绎)系统的任务.他对三巨头: Grunt, Gulp and NPM进行了比较,并讨论了每种的优缺点. By Nicolas ...
- Linux 系统Telnet服务
Linux 系统Telnet服务 telnet与ssh相比,安全性能并不高,但是在ssh版本升级或者其他的情况下还是需要开启这一项服务.linux提供服务是由运行在后台的守护进程daemon来执行的, ...
- DebugViewHierarchy
DebugViewHierarchy(视图调试)是XCode6新出的一项功能,它可以让开发者在程序运行时,动态的查看当前界面的显示情况,包括视图的层次,控件的大小和位置,而且会以3D效果显示当前视图的 ...
- 在 mongodb 终端环境下写多行 javascript 代码、函数
工作中碰到一个问题,需要把某个 collection 中的某些符合条件的数据取出来,逐行处理其中某些字段.mongodb 终端下支持直接写 js 代码.函数,也可以运行 js 文件.1 首先需要设置 ...