hiho 1 最长回文子串
来源:互联网 发布:天刀男性角色捏脸数据 编辑:程序博客网 时间:2024/05/21 01:43
问题描述:
一个字符串中连续的一段就是这个字符串的子串,而回文串指的是”12421”这种从前往后读和从后往前读一模一样的字符串,所以最长回文子串的意思就是这个字符串中最长的身为回文串的子串啦~
思路:
最笨的方法是:枚举字符串的每个位置,求出以当前位置为中心的回文子串的长度。
这个方法有两个问题:
1.只考虑了所有的长度为奇数的回文子串。
2.每次枚举的复杂度为O(n), 总体复杂度为O(
解法:
处理长度为偶数的子串
其实只要将所有的字符用特殊字符隔开,就可以用上面求奇数的算法处理偶数长度的情况了。
例如原始字符串为 “abaaba”,
预处理后为”#a#b#a#a#b#a#”
按照上面方法当枚举到第7个字符#时可以得到最长的回文子串(#a#b#a#a#b#a#),然后去掉所有的# 就是答案(abaaba)。O(n) 解法
1 中已经将答案为偶数的情况转换为奇数的求解,下面只考虑答案为奇数的情况。
笨方法中其实是有重复处理的,考虑下面的例子:序号 1 2 3 4 5 6 7 8 s(i) a b a b a b a c f(i) 0 1 2 3 2 ?第三行的f(i)表示以当前位置 i 为中心的最长回文子串边界到中心 i 的距离,例如i = 4时最长回文子串为 abababa, 右边界为s(7) = ‘a’, 所以f(4) = 7-4 = 3。
现在要求f(6) = ?,笨方法求解时就要比较s(5)和s(7)是否相等,但是我们可以发现在求4号的时候已经读入了 s(5)和s(7),也就是说s(5)和s(7)信息已经包含在了之前的 f(i) 中,如果我们能从之前的f(i)中判断出 s(5)=s(7),那么就可得出f(6) >= 1 的结论。
下面就来看看其中隐藏的关系:
由f(4) = 3可知
s(7) = s(1),s(5) = s(3)
要判断s(7) ? s(5) 等价于判断 s(1) ? s(3)
而f(2) =1,说明
s(1) = s(3)
所以 s(7) = s(5) 因此f(6)≥ 1
我们来总结一下上面的方法,首先我们选择了一个辅助点 j ,这个辅助点的选择条件是
使得f(j) + j 最大的j , 这意味着此j 的回文串右边界最大,也就是说在求f(j) 时读取了更靠右边的字符,这样 f(j)包含了更多靠后的字符信息。
辅助点j 的使用方法如下图所示图中绿色表示辅助点j 的最长回文串, i_mirror 是指以j为中心i的对称点
对于关于i对称的两个点 A 和 B 要判断 s(A) ? s(B) 我们可以将A B 以j 为中心找到其对应的对称点A’B’
由于A和A’ 在j 的回文串中 且关于j 对称
因此 s(A) = s(A’) 同理 s(B) = s(B’)
现在转换为要判断s(A’) ? s(B’)
A’ 和 B’ 是关于 i_mirror 对称的两个点(想想为什么),如果A’ 和 B’ 在i_mirror 的回文串中,就有
s(A’) = s(B’)
而这个判断等价于
f(i_mirror)≥ A’ - i_mirror = i - A = B - i
=> B-i≤ f(i_mirror) …………………….(1)
同时要保证 B和 B’在j 的最大回文子串中
f(j) + j≥ B
=> f(i)+j -i≥ B-i
=> B-i≤ f(i) +j -i ……………….. (2)
由(1), (2)两式可得:
B -i≤ min(f(i_mirror), f(j) + j - i)
取最大的B-i作为f(i) 的初始值
f(i) = min(f(i_mirror), f(j) + j - i) …………….(3)
这样就得到了一个较大的f(i) 然后再向两边扩展。
最后上代码
实现上的小技巧:
1. 在字符串的两端分别加上^ 和 $ 这样就不用考虑边界情况了。
2. 在加入# 后,求出的f() 值恰好为回文子串的长度。
3. 注意公式3 中f(i)可能< 0。
#include<cstdio>#include<cstring>#include<cmath>#include <algorithm>enum{maxn = 2000000+4};int n;char s[maxn], str[maxn];int f[maxn];#define OJint main(){ #ifndef OJ freopen("in.txt", "r", stdin); #endif // OJ scanf("%d", &n); while(n){ n--; scanf("%s", str); int t = 0; s[t++] = '^'; s[t++] = '#'; int k=0; while(str[k]){ s[t++] = str[k++]; s[t++] = '#'; } s[t++] = '$'; s[t++] = '\0'; int ans = 0; f[1] = 0; int j = 1; for (int i=2; i < t-1; i++){ int i_mirror = 2*j -i; f[i] = std::min(f[i_mirror], f[j] + j -i); f[i] = std::max(0, f[i]); while(s[i+f[i]] == s[i-f[i]]) f[i]++; f[i]--; if (f[i] + i > f[j] +j) { j = i; } ans = std::max(ans, f[i]); } printf("%d\n", ans); } return 0;}
复杂度分析:
首先如果公式3中求得的f(i_mirror) != f(j) + j - i 那么while(s[i+f[i]] == s[i-f[i]]) f[i]++; 将只执行一次(想想为什么)
如果f(i_mirror) == f(j) + j - i 那么 在i 处会继续扩展,while(s[i+f[i]] == s[i-f[i]]) f[i]++;将从
i+ f[i] = i+ f[j] + j -i = f[j] + j 处开始, 而f[j] + j 就是最当前读入的最右边字符,直到不相等为止。循环开始时f[i] + i = f[j] + j , 循环结束后有 f[i] + i
总体复杂度为O(n)。
- hiho 1 最长回文子串
- hiho 1 最长回文子串
- hiho-最长回文子串
- hiho一下第1周 最长回文子串
- hiho 第一周 最长回文子串
- 最长回文子串 - hiho一下
- hiho一下~week_1:最长回文子串
- hiho 1032 最长回文子串
- hiho#10362 最长回文子串
- Hiho 1032 最长回文子串
- 【hiho一下】第一周 最长回文子串
- hiho一下第一周 Hihocoder #1032 : 最长回文子串
- HiHo 1032 最长回文子串 (Manacher算法求解)
- hiho一下第一周:最长回文子串
- hiho一下第一周——最长回文子串
- hiho第一周——最长回文子串
- hiho一下第一周#1032 : 最长回文子串
- HiHo #1032 : 最长回文子串 【Manacher算法】
- 关于Git的撤销命令:如何在Git中撤销一切
- JSP/Servlet-----7、JSP的3个编译指令
- iOS讲解迷惑--Swift中函数的使用
- Linux_shell编程基础_source命令和点命令
- 利用pugixml 将xml转化为string
- hiho 1 最长回文子串
- 10进制转换成16进制
- 每天学点儿树莓派(番外一)——AirPlay播放器
- 博客数据库设计
- 策略模式
- JSP/Servlet-----8、JSP的7个内置标签
- iOS开发学习之C语言---C05 函数-2
- 对linux编译模块make -C path_to_kernel_src M=`pwd` modules的理解
- c语言中的数组,一维二维的概念和定义