【算法】最长回文子串 longest palindrome substring

对于字符串S, 要找到它最长的回文子串,能想到的最暴力方法,应该是对于每个元素i-th都向左向右对称搜索,最后用一个数组span 记录下相对应元素i-th为中心的回文子串长度。

那么问题来了:

  1. 这样的方法,对于奇回文子串和偶回文子串的处理不一样,比如所“acbca” 和“acbbca”

  2. 计算冗余,e.g. ”sscssabcccchccccba“中, 自左向右遍历计算的话,会发现, ”abcccchccccba“是对称的,而这个回文字符串里又左右对称分别包含了两个回文子字符”cccc“和”cccc“, 在对第二个”cccc“字符串遍历的时候,其实可以利用到”abcccchccccba“的对称性来直接赋值,而不用再次计算

于是,引出了Manacher's 算法:

  1. 为了可以对奇偶回文字符串不加区分地处理,对输入字符串增加边界元素(输入字符串S长度为N -> S2长度为2*N+1):e.g. ”aabbbcc“ -> "#a#a#b#b#b#c#c#" , ”aabbcc“ -> "#a#a#b#b#c#c#"

  2. 对于S2字符串,自左向右,每个字符逐个计算和处理

    1)用span[maxInputStringLength*2+1]数组来记录每个元素i-th为中心的回文字符串长度 : 初始化 span[0] = 0

    2)用center 和 rightBoundary 来记录上一个已知的回文字符串中心index和最右边界index :初始化 center = 0, rightBoundary = 0

    3)用 i 表示当前处理的元素, i2 表示 以 center 为中心的 center左侧 i 对称 元素下标 : i2 = center - (i - center)= center * 2 - i

    4)加上已知的回文子串信息之后,假设已经处理了S2[0, i-1]范围的元素,那么已知的回文子串中心center 必然属于[0, i-1]区间, 但是对于rightBoundary和i的关系可以有三种情况:

      a)当前处理的元素(index = i)以 center为中心,对称的元素(index = i2)上有,span[i2] < rightBoundary - i - 1, 表示i2点的回文字符串最左端不会超过leftBoundary的长度,利用了已知的回文字符串信息辅助说明,当前以i为中心的回文字符串将和i2完全对称,span[i] = span[i2]

      

      b)当前处理的元素(index = i)以 center为中心,对称的元素(index = i2)上有,span[i2] >= rightBoundary - i - 1, 表示 i2 点的回文字符串最左端已经超过了leftBoundary的范围,不能利用已知回文字符串直接赋值,但还是可以利用对称性,使得对称搜索从rightBoundary处开始,这样可以减少计算

      

      c)当前处理的元素(index = i)并不在已知回文串的记录范文内,那么久没有辅助信息,需要从i元素的左右对称搜索,得到span[i]的值

      

    5)最后要更新center和rightBoundary的值,使得已知回文字符串的覆盖范围右移,这样可以持续辅助搜索

  

 1 const int maxStringLength = 1000;
 2 
 3 string longestPalindrome(string s) {
 4 
 5     if (s.empty()) return "";
 6 
 7     ///add boundary
 8     string s2;
 9     for (int i = 0; i < s.size(); i++) {
10         s2.push_back('#');
11         s2.push_back(s[i]);
12     }
13     s2.push_back('#');
14 
15 
16     /// setting
17     int span[maxStringLength] = {0};
18     span[0] = 0; // init the first element's length of palindrome
19     int center = 0, rightBoundary = 0;
20     int left = 0, right = 0;
21 
22     ///traverse 
23     for (int i = 1; i < s2.size(); i++) {
24         if (i <= rightBoundary) {
25             int i2 = center * 2 - i;// center - (i - center)
26             if (span[i2] < (rightBoundary - i - 1)) {
27                 span[i] = span[i2];
28                 left = -1;
29             }
30             else {
31                 span[i] = rightBoundary - i;
32                 right = rightBoundary + 1;
33                 left = i * 2 - right; //i - (right - i)
34             }
35         }
36         else {
37             span[i] = 0;
38             left = i - 1;
39             right = i + 1;
40         }
41 
42         while (left >= 0 && right < s2.size() && s2[left] == s2[right]) {
43             span[i] ++;
44             left--;
45             right++;
46         }
47 
48         if ((i + span[i]) > rightBoundary) {
49             center = i;
50             rightBoundary = i + span[i];
51         }
52     }
53 
54     /// find the max span length
55     int maxSubstringCenter = 0, maxSubstringLen = 0;
56     for (int i = 0; i < s2.size(); i++) {
57         if (span[i] > maxSubstringLen) {
58             maxSubstringLen = span[i];
59             maxSubstringCenter = i;
60         }
61     }
62 
63     /// remove boundary '#' from substring
64     string result;
65     for (int i = maxSubstringCenter - maxSubstringLen; i <= maxSubstringCenter + maxSubstringLen; i++) {
66         if (s2[i] != '#')
67             result.push_back(s2[i]);
68     }
69 
70     return result;
71 }
View Code
原文地址:https://www.cnblogs.com/cheermyang/p/6674784.html