Longest Palindrome 最长回文串问题
1.题目
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1: Input: "babad" Output: "bab" Note: "aba" is also a valid answer.
Example 2: Input: "cbbd" Output: "bb".
题目的大意是求取给定字符串中最长的回文子串(palindromic substring),顺带限定了字符串的最大长度(1000),简化编码不用malloc了,不过无关紧要.
2.思路
最原始的做法,暴力搜索:找出字符串s的所有可能子串,判断每个子串是否回文串.其思路比较简单,直接贴代码:
int
is_palindrome(char *s, int begin, int end) {
while (begin <= end && s[begin] == s[end])
++begin, --end;
if (begin < end)
return ;
else
return ;
} char *longestPalindrome(char *s) {
int i, step;
int max, begin; if (strlen(s) < )
return s; max = ;
begin = ;
for (i = ; i < strlen(s); ++i) {
for (step = ; step <= strlen(s) - i - ; ++step) {
if (is_palindrome(s, i, i + step)) {
if (step > max) {
begin = i;
max = step;
}
}
}
}
s[begin + max + ] = ;
return &s[begin];
}
对于一个长度为n的字符串,其子串(不含单个字符的情形)共有n*(n-1)/2,因为一组下标(begin,end)表达了一个子串,从0,1,2,...,n-1个数中选出两个数,就是个排列组合问题.对于每个子串,判断其是否回文串,时间复杂度Ο(n),所以暴力破解的时间复杂度是Ο(n3).
很显然暴力搜索的方法是可能优化的.如果在求解一个问题的时候,所有的已知条件都使用了,那么这个解法应该不太可能优化了,如果还存在未使用的条件,那说明解法还存在优化的可能.有些时候这些条件是很明显的,而有些时候却隐藏的很深,甚至需要复杂的数学推导(个人的一点心得,有不对的,轻拍).回到正题,那么在暴力破解法中有哪些条件是我们还未使用的呢?例如知道"bab"是回文串,在判断"xbabx"时还有必要遍历吗?显然只需要比较两端的字符是否相等即可.
3.空间换时间
为了判断子串是否是回文串,我们可以利用已有的回文串信息简化操作,这意味着我们需要将已经找到的回文串信息记录下来.这是一种典型的空间换时间的做法.思路有了,如何实现呢?
对于一个回文子串,有三个信息:起始下标,结束下标,长度(或者认为是两个信息,长度可以用起止坐标计算).设L(i,j)表示一个回文子串的长度,S[i]表示下标i处的字符,根据我们的分析有以下结论:
L(i,j) = L(i+1,j-1) + 2; if S[i] == S[j]
特别地,L(i,i) = 1, L(i,i+1) = 2 if S[i] == S[i+1]. 有了这个结论,我们就可以用一个二维数组P[i][j]保存已知的回文子串信息来提高效率(你可能习惯称这种方法为动态规划(dynamic programming)).
/* 采用动态规划方式求解最长回文子串.
* 设P(i,j)为回文子串的长度,则有如下关系:
* P(i,j) = P(i+1,j-1) + 2; if S[i] == S[j]
* 特别地, P(i,i) = 1; P(i,i+1) = 2 if S[i] == S[i+1]
*/
char *
longestPalindrome(char *s) {
int P[][] = {};
int i, j, maxi, maxj, max;
size_t len; len = strlen(s);
if (len < )
return s; max = ;
maxi = maxj = ;
for (j = ; j < len; ++j) {
for (i = ; i <= j; ++i) {
if (i == j)
P[i][j] = ;
else if (i + == j && s[i] == s[j])
P[i][j] = ;
else if (i + < j && P[i+][j-] > && s[i] == s[j])
P[i][j] = P[i + ][j - ] + ; if (P[i][j] > max) {
max = P[i][j];
maxi = i;
maxj = j;
}
}
}
s[maxj + ] = ;
return &s[maxi];
}
上述代码可以将更新max(最长回文子串的长度)的操作放在if语句分支里面减少不必要的判断,这里为了代码简洁没有这么做.这种解法相比于暴力破解法的优势是提高了判断一个子串是否回文串的效率(Ο(n)变为Ο(1)),故整个算法的时间复杂度是Ο(n2).
是否到此为止了呢?是否还有优化的可能或者说是否还存在未使用的已知条件?当然是有的(不然就不会有这篇博客了XD).所谓回文串是反转后和原字符串相同的字符串(等等,这个条件我们前面两种方法不都用到了吗?确实是用了,但并未有效或者说完全利用.),对于每一个回文子串S(i,j),可以假想其有一个中点下标是c,有一个半径r=L(i,j)/2,中点两边距中点距离相等的字符必然相等,这是一种对称性,利用这个性质我们能简化下标在(c, c+r)范围内的每个点的最大回文串长度的计算吗?答案是YES!
4.Manacher算法
关于这个算法的详细说明我就不班门弄斧了,附上两个链接,一个e文,一个中文,图文并茂,个人认为讲的都比较清楚.
最后附上自己的实现作为结束:
/* Manacher's Algorithm
* 关键思想是利用回文字符串的对称性,为了简化处理字符串长度的奇偶问题,
* 对原字符串进行了预处理.对于处理后的字符串为了简化边界处理,在起始
* 位置设置了哨兵,结束位置采用'\0'作为哨兵.
*/ #define min(x, y) ((x) > (y) ? (y) : (x)) char *
preProcess(char *s) {
size_t len, i;
char *ret; len = strlen(s);
ret = malloc(( * len + ) * sizeof(char));
memset(ret, '#', * len + );
/* 设置哨兵,简化计算P时的越界检查,设置的哨兵不能等于原字符串中
* 任意一个字符.
*/
ret[] = '^';
for (i = ; i <= len; ++i)
ret[ * i] = s[i - ];
ret[ * len + ] = ; return ret;
} char *
longestPalindrome(char *s) {
int *P;
size_t len, max, center, right, i, mirror;
char *ptr; len = strlen(s);
if (len < )
return s; ptr = preProcess(s);
P = calloc( * len + , sizeof(int)); /* 比处理后的字符串少一个结束符'\0' */ center = ; /* 回文字符串的中点 */
right = ; /* 回文字符串的右边界 */
for (i = ; i < * len + ; ++i) {
mirror = * center - i; /* center - (i - center) */ /* 利用回文字符串的对称性快速计算i处的回文字符串长度:
* i处关于中点center的对称点mirror处的回文字符串长度P[mirror]小于i到边界right的距离,
* 则P[i] = P[mirror],否则P[i] = right - i.
*/
P[i] = (right > i) ? min(right - i, P[mirror]) : ; /* 往右搜索扩展回文串,因为ptr[0]设置了哨兵故无需担心数组越界 */
while (ptr[i + P[i] + ] == ptr[i - P[i] - ])
++P[i]; /* 更新回文串的中点和右边界 */
if (i + P[i] > right) {
center = i;
right = i + P[i];
}
}
/* 返回最长的回文串 */
max = ;
for (i = ; i < * len + ; ++i) {
if (P[i] > max) {
max = P[i];
center = i;
}
}
free(P);
free(ptr);
s[(center + max) / ] = ;
return &s[(center - max) / ];
}
Longest Palindrome 最长回文串问题的更多相关文章
- [LeetCode] Longest Palindrome 最长回文串
Given a string which consists of lowercase or uppercase letters, find the length of the longest pali ...
- 409 Longest Palindrome 最长回文串
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串.在构造过程中,请注意区分大小写.比如 "Aa" 不能当做一个回文字符串.注意:假设字符串的长度不会超过 ...
- [LeetCode] 409. Longest Palindrome 最长回文
Given a string which consists of lowercase or uppercase letters, find the length of the longest pali ...
- LeetCode409Longest Palindrome最长回文串
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串. 在构造过程中,请注意区分大小写.比如 "Aa" 不能当做一个回文字符串. 注意: 假设字符串的长度不 ...
- POJ----(3974 )Palindrome [最长回文串]
Time Limit: 15000MS Memory Limit: 65536K Total Submissions: 5121 Accepted: 1834 Description Andy ...
- 字符串的最长回文串:Manacher’s Algorithm
题目链接:Longest Palindromic Substring 1. 问题描述 Given a string S, find the longest palindromic substring ...
- Leetcode0005--Longest Palindromic Substring 最长回文串
[转载请注明]http://www.cnblogs.com/igoslly/p/8726771.html 来看一下题目: Given a string s, find the longest pali ...
- Manacher(输出最长回文串及下标)
http://acm.hdu.edu.cn/showproblem.php?pid=3294 Girls' research Time Limit: 3000/1000 MS (Java/Others ...
- Java实现 LeetCode 409 最长回文串
409. 最长回文串 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串. 在构造过程中,请注意区分大小写.比如 "Aa" 不能当做一个回文字符串. 注意 ...
随机推荐
- IntelliJ IDEA自动导入包去除星号(import xxx.*)
打开设置>Editor>Code Style>Java>Scheme Default>Imports ① 将Class count to use import with ...
- Https网络安全架构设计
一.对称加密 对称密钥加密(Symmetric-key algorithm)又称对称加密.私钥加密.共享密钥加密,是密码学中的一类加密算法.这类算法在加密和解密时使用相同的密钥,或是使用两个可以简单地 ...
- Spring Cloud(Dalston.SR5)--Config 集群配置中心-加解密
实际应用中会涉及很多敏感的数据,这些数据会被加密保存到 SVN 仓库中,最常见的就是数据库密码.Spring Cloud Config 为这类敏感数据提供了加密和解密的功能,加密后的密文在传输给客户端 ...
- 基于scrapy源码实现的自定义微型异步爬虫框架
一.scrapy原理 Scrapy 使用了 Twisted异步网络库来处理网络通讯.整体架构大致如下 Scrapy主要包括了以下组件: 引擎(Scrapy)用来处理整个系统的数据流处理, 触发事务(框 ...
- [转摘]VMware下Windows系统出现大量可删除ATA Channel的解决办法
编辑VMX配置文件加上一句话就可以了 devices.hotplug = "false" 原文:http://blog.ihipop.info/2015/05/4830.html
- Git使用,将本地项目推送到GitHub上
首先本地仓库中创建一个项目 ex: proA 在远程github仓库中创建项目 ex: proA 在本地仓库proA下打开terminal 使用命令: 1.git add * 2.git commit ...
- 移植mysql到ARM(AM335x)
一,编译ncurses 编译mysql需要依赖ncurses,先编译ncurses 1.下载ncurses 下载路径是ftp://ftp.gnu.org/gnu/ncurses,选择下载的是ncurs ...
- 微信小程序布局
尺寸单位与设计原则 首先,我们现在页面中引入一张图片 但是实际上,这个图片的大小是32*18的,之所以会显示这么大,是因为image组件默认的宽度为300px,默认的高度为225px,如果我们需 ...
- winrar+目录穿透复现
前言: 学习下该漏洞,记录下这是自动化复现,没有具体分析.菜逼只会用. 00x1: 漏洞简单描述: 该漏洞事一个由UNACEV2.dll代码库中的一个深藏已久的漏洞 当攻击者制作一个恶意的ACE文件时 ...
- 数学模块_math
ceil 进一, 向上取整 floor 向下取整 pow(x, y) x的y次方 print(math.pow(2, 3)) # 8.0 sqrt(x) x的开平方(结果为浮点数) print(mat ...