【算法题】Manacher算法及其扩展
来源:互联网 发布:java bigdecimal最大值 编辑:程序博客网 时间:2024/06/15 14:26
2017/11/21
Manacher问题
1、Manacher问题
1.1 问题描述:
找出字符串str中最长的回文子串
1.2 思路
1、在解决最长回文子串问题前,要解决奇回文和偶回文的问题。我们在判断奇回文时,是根据一个字符串,然后同时向两边扩展;偶回文则是直接向两边扩展,中间没有字符串。如下:
12a21 奇回文1221 偶回文
为了解决这个问题,将原始字符串进行改进,在原始字符串的开头、结尾以及字符之间添加一个字符,如“#”,作为一个虚轴。然后使用新的字符串。这样做不会改变原有的回文结构,而且新字符串中所有的回文变为奇回文。
eg:
"123abx""#1#2#3#a#b#x#"
2、然后是具体的Manacher算法,首先给出几个概念。
回文边界R:字符串最右回文边界。
回文中心C:字符串最右回文边界R的中心,随R变动。
回文半径数组radius[]:记录每个字符以其为中心的回文半径大小。
3、Manacher一共分了四种情况讨论。
当遍历到i时,如果i在R的右边,则只能采取暴力匹配方式,直接以其为中心查找回文。
当遍历到i时,如果i不在R的右边,则又分为3种情况:
①i关于回文中心C的对称点i’的回文边界在C的回文边界中,则可知i的回文边界也在C的回文边界里,不会再向外扩,解释如图:
②i关于回文中心C的对称点i’的回文边界在C的回文边界外(左边界超出),则可知i的回文右边界刚好在C的回文右边界上,不会再向外扩,解释如图(图中左边是i’,不小心写错了):
③i关于回文中心C的对称点i’的回文边界在C的回文边界上(左边界与左边界重合),则可知i已有的回文在C的边界中,但是超过R之后,i是否有能更大的回文,无法得知,只能采用暴力扩的方式,解释如图:
4、所以综上,在代码处理上,一共分了4种情况,在情况2、3上其回文半径就是i’的回文半径radius[2 * C - i]与R - i其中较小的那一个。
1.3 代码
#include <iostream>#include <string>#include <vector>using namespace std;/*2017/11/18Manacher算法:找出字符串str中最长的回文子串*/#if 1#define max(a,b)(a>b?a:b)#define min(a,b)(a<b?a:b)string newString(string s){ string news = "#"; for (int i = 0; i < s.length();i++) news = news + s[i] + "#"; return news;}//都用奇回文解决string Manacher(string s){ if(s.length()<2) return s; int C = -1;//最右回文半径的回文中心 int R = -1;//最右回文边界的下标,但不包括在回文中 int imax = INT_MIN;//最大回文半径 int ic = 0;//最大回文半径的回文中心 string news = newString(s); vector<int>radius(news.length(), 0); for (int i = 0; i < news.length(); i++) { radius[i] = 1;//默认从左右两边起第一个开始比较 radius[i] = R > i ? min(radius[2 * C - i], R - i) : radius[i]; while (i-radius[i]>=0 && i+radius[i]<news.length())//不能越过字符串边界 { if (news[i - radius[i]] == news[i + radius[i]])//R>i不会进入,直接break radius[i]++; else break; } if (i + radius[i] > R) { C = i; R = i + radius[i]; } if (radius[i] > imax) { imax = radius[i]; ic = C; } } imax = imax - 1;//imax最后++时多加了一个1,要减掉,得到原字符串的最大回文长度 return s.substr((ic - imax) / 2, imax);}void main(){ string s1 = "abc1234321ab"; cout << Manacher(s1) << endl; string s2 = "1234321ab"; cout << Manacher(s2) << endl; system("pause");}#else#endif
1.4 结果截图
2、Manacher问题扩展
2.1 问题描述:
给定一个字符串str1,只能往str1的后面添加字符变成str2,要求str2
整体都是回文串且最短。
2.2 思路
1、只要求出包含了str1中最后一个字符的最长回文子串s即可。
2、然后将子串s在str1中之前的字符逆序添加到str1末尾即可。
如“abc12321”,包含“1”的最长回文子串是“12321”,将“abc”逆序添加到“abc12321”末尾,变成“abc12321cba”,即为所求的str2。
2.3 代码
#include <iostream>#include <string>#include <vector>using namespace std;/*2017/11/18Manacher算法扩展:给定一个字符串str1,只能往str1的后面添加字符变成str2,要求str2整体都是回文串且最短。*/#if 1#define max(a,b)(a>b?a:b)#define min(a,b)(a<b?a:b)string newString(string s){ string news = "#"; for (int i = 0; i < s.length(); i++) news = news + s[i] + "#"; return news;}//都用奇回文解决int Manacher(string s){ int C = -1; int R = -1; string news = newString(s); vector<int>radius(news.length()); for (int i = 0; i < news.size();i++) { radius[i] = R>i ? min(radius[2 * C - i], R - i) : 1; while (i-radius[i]>=0 && i+radius[i]<news.length()) { if (news[i - radius[i]] == news[i + radius[i]]) radius[i]++; else break; } if (i + radius[i] > R) { R = i + radius[i]; C = i; if (R == news.length()) return radius[i] - 1; } }}string ShortestEnd(string s){ int r = Manacher(s); string s_short = s; for (int i = s.length() - r - 1; i >= 0; i--) s_short += s[i]; return s_short;}void main(){ string s1 = "abc1234321"; cout << ShortestEnd(s1) << endl; string s2 = "abb3"; cout << ShortestEnd(s2) << endl; system("pause");}#else#endif
2.4 结果截图
- 【算法题】Manacher算法及其扩展
- 详解manacher算法,及其扩展
- 【算法】(扩展)KMP+manacher
- manacher算法及扩展
- manacher算法及其应用
- Manacher算法及其Java实现
- 欧几里德算法及其扩展算法
- 欧几里德算法及其扩展算法
- 欧几里得算法及其扩展
- 欧几里德算法及其扩展
- 欧几里得算法及其扩展
- EM算法及其扩展
- Manacher算法
- Manacher算法
- Manacher算法
- Manacher算法
- Manacher 算法
- manacher算法
- 小米推送,华为推送,个推,阿里云推送集成(服务端JAVA开发)
- 关于计算机学习的总结
- echarts2的一个地图demo
- matlab 爬虫抓取A股当日每笔成交数据
- 分享一个牛人给java初学者的建议(图文完整版)
- 【算法题】Manacher算法及其扩展
- mysql安装完成后修改默认密码
- jdk的环境变量配置
- DNS内置命令rndc用法
- Docker安全(翻译)
- vs2013编译osg缺少mfc120d.lib
- AndrewNg神经网络和深度学习笔记-Week3-6激活函数
- 最近积累的Android的一个工具类合集
- android broadcastreceiver 重复创建和无法接受消息问题