最长回文Manacher

预处理:

判断一个串是不是回文串,往往要分开编写,造成代码的拖沓

  1. int LongestPalindrome(const char * s, int n){
  2. int i, j, max;
  3. if (s == 0 || n < 1)
  4. return 0;
  5. max = 0;
  6. for (i = 0; i < n; ++i){//i is the middle point of palindrome
  7. for (j = 0; (i - j >= 0) && (i + j < n);++j)// for the lenth of palindrome is odd
  8. if (s[i - j] != s[i + j])
  9. break;
  10. if (--j * 2 + 1>max) max = j * 2 + 1;
  11. for (j = 0; (i - j >= 0) && (i + j + 1 < n); ++j)//for the even case
  12. if (s[i - j] != s[i + j + 1])
  13. break;
  14. if (--j * 2 + 2 > max)
  15. max = j * 2 + 2;
  16. }
  17. return max;
  18. }
  19. void LongestPalindrome_test(){
  20. char s[] = "aba";
  21. int length = LongestPalindrome(s,strlen(s));
  22. cout << length << endl;
  23. }

上面的循环中,对于回文长度本身的奇偶性,我们进行区别处理。这样有点拖沓。我们根据一个简单的事实:长度为n的字符串,共有n-1个“邻接”,加上首字符的前面后某字符的后面,共有n+1的“空(gap)”。因此,字符串本身和gap一起,共有2n+1个,必定是奇数。

  • abba --> #a#b#b#a# 此回文这样处理后长度4-->9
  • aba --> #a#b#c# 此回文这样处理后长度3-->7
    因此,将待计算母串扩展成gap串,则里面的回文字串的长度都变成了奇数,我们计算的时候只用考虑奇数匹配即可。

数组 int P[size]:

字符串12212321--> S[] = "$#1#2#2#1#2#3#2#1#";( 为了统一处理,最前面加一个特殊字符。则下标从1开始)
用一个数组P[i]来记录以字符S[i] 为中心的最长回文字串向左/向右扩张的长度(包括S[i]),比如S和P的对应关系:

  1. S: "# 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #";
  2. P: {1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1}

P[i]-1正好是原字符串中回文串的总长度。

“# 1 # 2 # 2 # 1 #”
“---------1 2 3 4 5”
P[i] = 5
原始回文字符串长度是偶数的情况,则在gap串中P[i]的值多了中间的一个#。P[i]-1 == length

"# 1 # 2 # 3 # 2 # 1 # "
"---------------------1 2 3 4 5 6 "
p[i] = 6
原始回文字符串长度是奇数的情况,则在gap串种P[i]的值多了最后的#号。P[i]-1 == length
目前,只要知道了数组P的值,就能求得最多长回文的大小了。但是,怎么求呢P呢?请看下文。

如何求数组P:

  1. S: "# 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #";
  2. P: {1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1}

我们的任务,假定已经得到了前i个值,考察i+1如何计算。即:在P[0...i-1]已知的情况下,利用其中信息,计算P[i]的值。

  1. 通过简单遍历,找到i个三元组{k,P[k],k+p[k]},0<=k<=i-1。(以k为中心的字符形成的最大回文子串的最右位置是k+p[k]-1)
  2. 以k+p[k]为关键字,挑选出这i个三元组中,k+p[k]最大的那个三元组,不妨记作(id,P[id],id+P[id])。进一步,为了简化,记mx=P[id]+id,因此,得到三元组为(id,P[id],mx),这个三元组的含义非常明显:所有i个三组中,向右达到最远的位置,就是mx。
  3. 在计算P[i]的时候,考察i是否落在了区间[0,mx)中;
    -1. 若i在mx的右侧,说明[0,mx)没有能够控制住i,P[0..i-1]都已知,无法给P[i]的计算带来有价值的信息。
    -2. 若i在mx的左侧,说明[0,mx)控制(也有可能部分控制)了i,现在以图示来详细考察这种情况。这就是Manacher递推关系。

Manacher递推关系:

  1. 记i关于id的对称点位j(j=2*id - i),若此时满足条件mx-iP[j].
    记my为mx关于id的对称点();
    由于以S[id]为中心的最大回文字串为S[my+1...id...mx-1],
    即:S[my+1....,id]与S[id,...,mx-1]对称,而i和j关于id对称,因此P[i]=Pj。
    840348-20151118194047061-2112062112.png

  2. 记i关于id的对称点位j(),若此时满足条件mx-iP[j]:
    记my为mx关于id的对称点();
    由于以S[id]为中心得最大回文字串为S[my+1,...id...mx-1],即S[my+1,...,id]与S[id,...,mx-1]对称,而i和j关于id对称,因此P[i]至少等于mx-i(途中绿框部分)
    840348-20151118194050421-1395520371.png


Manacher代码

  1. void Manacher(char * s, int *p){
  2. int size = strlen(s);
  3. p[0] = 1;
  4. int id = 0;
  5. int mx = 1;
  6. for (int i = 1; i < size; i++){
  7. if (mx > i) p[i] = min(p[2 * id - i], mx - i);
  8. else p[i] = 1;
  9. /*
  10. 这里计算s[i]处实际回文的长度,
  11. 如果没有manacher算法,则这里p[i]每次都是从1开始
  12. 而manancher算法利用了P[0...i-1]的信息,让i可能从大于1的地方开始*/
  13. for (; s[i - p[i]] == s[i + p[i]]; p[i]++);
  14. if (mx < i + p[i]){
  15. mx = i + p[i];
  16. id = i;
  17. }
  18. }
  19. }
  20. void Manacher_test(){
  21. char * s = "abbca2r";
  22. int size = (int)strlen(s);
  23. cout << s << endl;
  24. int sizenew = size * 2 + 2;//加上开始的$
  25.     char * snew = new char[sizenew + 1];//别忘了最后的\0
  26.     snew[0] = '$';
  27. int j = 0;
  28. for (int i = 1; i < 2 * size + 2; i++){
  29. if (i % 2 == 0)
  30. snew[i] = s[j++];
  31. else
  32. snew[i] = '#';
  33. }
  34. snew[sizenew] = '\0';
  35. cout << snew << endl;
  36. int * p = new int[sizenew];
  37. Manacher(snew,p);
  38. Print(p,sizenew);
  39. }

Manacher的改进

p[j] > mx - i: p[i] = mx - i
p[j] < mx - i: p[i] = p[j]
p[j] = mx - i: p[i] >= p[j]
根据上面的源码,原始Manacher算法,前两个等号都是大于等于。
下面是改进的Manacher算法:

  1. void Manacher2(char * s, int * p){
  2. int size = strlen(s);
  3. p[0] = 1;
  4. int id = 0;
  5. int mx = 1;
  6. for (int i = 1; i < size; i++){
  7. if (mx>i){
  8. if (mx > i){
  9. if (p[2 * id - 1] != mx - i){
  10. p[i] = min(p[2*id - i],mx-i); //能确定p[i]的值
  11. }
  12. else{
  13. p[i] = p[2*id - i];
  14. for (; s[i - p[i]] == s[i + p[i]]; p[i]++);
  15. }
  16. }
  17. }
  18. else{
  19. p[i] = 1;
  20. for (; s[i - p[i]] == s[i + p[i]]; p[i]++);
  21. }
  22. if (mx < i + p[i]){
  23. mx = i + p[i];
  24. id = i;
  25. }
  26. }
  27. }


来自为知笔记(Wiz)


转载于:https://www.cnblogs.com/gavin-yue/p/4975665.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/419304.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

链表面试笔试题目总结

链表是最基本的数据结构&#xff0c;凡是学计算机的必须的掌握的&#xff0c;在面试的时候经常被问到&#xff0c;关于链表的实现&#xff0c;百度一下就知道了。在此可以讨论一下与链表相关的练习题。 1、在单链表上插入一个元素&#xff0c;要求时间复杂度为O(1) 解答&#x…

linux下常用FTP命令

linux下常用FTP命令 1. 连接ftp服务器 1. 连接ftp服务器格式&#xff1a;ftp [hostname| ip-address]a)在linux命令行下输入&#xff1a; ftp 192.168.1.1b)服务器询问你用户名和密码&#xff0c;分别输入用户名和相应密码&#xff0c;待认证通过即可。 2. 下载文件 下载文件通…

线程、多线程与线程池

线程&#xff1a;进程中负责程序执行的执行单元。一个进程中至少有一个线程。 多线程&#xff1a;解决多任务同时执行的需求&#xff0c;合理使用CPU资源。多线程的运行是根据CPU切换完成&#xff0c;如何切换由CPU决定&#xff0c;因此多线程运行具有不确定性。 线程池&…

selenium webdriver python 元素操作

常用操作 click 点击某个元素driver.find_element_by_id(“su”).click()cleardriver.find_element_by_id(“kw”).clear()send_keys模拟键盘输入driver.find_element_by_id(“kw”).send_keys(“hello”)from selenium.webdriver.common.keys import Keysdriver.find_element_…

通过mvn dependency:tree 查看依赖树,解决依赖jar冲突问题

举例&#xff1a;E:\01workspace\chenxh\09research\rop\rop>mvn dependency:tree [WARNING] [WARNING] Some problems were encountered while building the effective settings [WARNING] pluginRepositories.pluginRepository.id must not be local, this identifier is …

“睡服”面试官系列第八篇之iterator(建议收藏学习)

目录 1. Iterator&#xff08;遍历器&#xff09;的概念 2. 默认 Iterator 接口 3. 调用 Iterator 接口的场合 3.1解构赋值 3.2扩展运算符 3.3yield* 3.4其他场合 4. 字符串的 Iterator 接口 5. Iterator 接口与 Generator 函数 6. 遍历器对象的 return()&#xff0…

【转】使用Jmeter针对ActiveMQ JMS Point To Point压力测试

准备工作针对JMS类型的Sampler&#xff0c;需要额外的jar包&#xff08;这里用的是apache ActiveMQ&#xff0c;将下载的AMQ apache-activemq-5.5.0根目录下的activemq-all-5.5.0.jar拷贝到JMETER_HOME\lib目录下&#xff09;启动ActiveMQ&#xff1a;打开dos窗口&#xff0c;进…

Mysql中int(M)的含义

知识点&#xff1a;int(M) M指示最大显示宽度。最大有效显示宽度是255。显示宽度与存储大小或类型包含的值的范围无关 所以int(10)与int(11)后的括号中的字符表示显示宽度&#xff0c;整数列的显示宽度与mysql需要用多少个字符来显示该列数值&#xff0c;与该整数需要的存储空…

用户体验——南通大学教务学生管理系统

进入主界面&#xff0c;看见了这么长的菜单&#xff0c;我就感到到了一丝恶意&#xff0c;然后我想用这个系统来查询我的公选课&#xff0c;我把菜单拉到底&#xff0c;发现我需要打五个字母&#xff0c;我想没关系&#xff0c;照着打就好了。 于是我点击输入栏&#xff0c;准备…

“睡服”面试官系列第九篇之数值的扩展(建议收藏学习)

目录 1. 二进制和八进制表示法 2. Number.isFinite(), Number.isNaN() 3. Number.parseInt(), Number.parseFloat() 4. Number.isInteger() 5. Number.EPSILON 6. 安全整数和 Number.isSafeInteger() 7. Math 对象的扩展 7.1Math.trunc() 7.2Math.sign() 7.3Math.cbr…

MySQL分页查询小技巧

当提到查询数据库中某个指定起始位置到特定位置的数据时&#xff0c;我们的第一个反应是采用如下的语法格式&#xff1a; limit是mysql的语法 select * from table limit m,n 其中m是指记录开始的index&#xff0c;从0开始&#xff0c;表示第一条记录 n是指从第m1条开始&#x…

转:vim----复制粘贴

vim有12个粘贴板&#xff0c;分别是0、1、2、...、9、a、“、&#xff0b;&#xff1b;用:reg命令可以查看各个粘贴板里的内容。在vim中简单用y只是复制到“&#xff08;双引号)粘贴板里&#xff0c;同样用p粘贴的也是这个粘贴板里的内容&#xff1b; 要将vim的内容复制到某个…

个域名最多能对应几个IP地址?,一个IP地址可以绑定几个域名?

1、也就是说通常情况下一个域名同一时刻只能对应一个IP地址。 但是在域名服务商那里&#xff0c;你可以把服务器群里面的多个提供相同服务的服务器IP设置一个域名可以轮询。但是同一时刻&#xff0c;一个域名只能解析出一个IP供你使用。这些IP可以轮流着被解析。 这些IP其实对应…

每个 Linux 用户都应该知道的命令行技巧

每个 Linux 用户都应该知道的命令行技巧 原创 2015-09-11 伯乐在线 程序员的那些事 【伯乐在线导读】&#xff1a;有网友在问答网站Quora上提问&#xff1a;“有哪些省时小技巧&#xff0c;是每个Linux用户都应该知道的&#xff1f;” Joshua Levy 平常就在 Linux 平台工作&…

Linux文件权限一共10位长度,分成四段,每段的含义

Linux用户分为&#xff1a;拥有者、组群(Group)、其他&#xff08;other&#xff09; linux中的文件属性过分四段&#xff0c;如 -rwzrwz--- 第一段 - 是指文件类型 表示这是个普通文件 文件类型部分 -&#xff1a;文件 d&#xff1a;文件夹 l&#xff1a;链接文件&#xff…