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文,一个中文,图文并茂,个人认为讲的都比较清楚.

Longest Palindromic Substring

最长回文子串

最后附上自己的实现作为结束:

/* 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 最长回文串问题的更多相关文章

  1. [LeetCode] Longest Palindrome 最长回文串

    Given a string which consists of lowercase or uppercase letters, find the length of the longest pali ...

  2. 409 Longest Palindrome 最长回文串

    给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串.在构造过程中,请注意区分大小写.比如 "Aa" 不能当做一个回文字符串.注意:假设字符串的长度不会超过 ...

  3. [LeetCode] 409. Longest Palindrome 最长回文

    Given a string which consists of lowercase or uppercase letters, find the length of the longest pali ...

  4. LeetCode409Longest Palindrome最长回文串

    给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串. 在构造过程中,请注意区分大小写.比如 "Aa" 不能当做一个回文字符串. 注意: 假设字符串的长度不 ...

  5. POJ----(3974 )Palindrome [最长回文串]

    Time Limit: 15000MS   Memory Limit: 65536K Total Submissions: 5121   Accepted: 1834 Description Andy ...

  6. 字符串的最长回文串:Manacher’s Algorithm

    题目链接:Longest Palindromic Substring 1. 问题描述 Given a string S, find the longest palindromic substring ...

  7. Leetcode0005--Longest Palindromic Substring 最长回文串

    [转载请注明]http://www.cnblogs.com/igoslly/p/8726771.html 来看一下题目: Given a string s, find the longest pali ...

  8. Manacher(输出最长回文串及下标)

    http://acm.hdu.edu.cn/showproblem.php?pid=3294 Girls' research Time Limit: 3000/1000 MS (Java/Others ...

  9. Java实现 LeetCode 409 最长回文串

    409. 最长回文串 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串. 在构造过程中,请注意区分大小写.比如 "Aa" 不能当做一个回文字符串. 注意 ...

随机推荐

  1. MySQL更改命令行默认分隔符

    MySQL命令行默认语句分隔符为分号  ; 使用DELIMITER命令可以更改默认分隔符 mysql> DELIMITER   // 将默认分割符改为  //

  2. Python 标准库笔记(1) — String模块

    原文出处: j_hao104 String模块包含大量实用常量和类,以及一些过时的遗留功能,并还可用作字符串操作. 1. 常用方法 常用方法 描述 str.capitalize() 把字符串的首字母大 ...

  3. 关于DDS的基础知识

    关于DDS的基础知识 声明:这篇博客是我抄别人的,为什么想抄呢,因为他把DDS中的核心部件,相位累加器以及正弦ROM查找表寻址讲得明明白白,我真的好想把这篇文章据为己有. DDS是直接数字式频率合成器 ...

  4. 面向对象:静态属性,静态方法,组合,继承,衍生,继承之mro线性顺序列表,面向对象综合实例

    1.静态属性(附有装饰器) class Room: def __init__(self,name,owner,width,length,height): self.name=name self.own ...

  5. 转 Ubuntu16.04+QT4.8.7开发环境搭建

    Qt安装步骤1.安装g++以及依赖库 sudo apt-get install g++  sudo apt-get install g++-multilib libx11-dev libxext-de ...

  6. Ubuntu16.04安装Python3.6 和pip(python3 各版本切换)

    安装: sudo add-apt-repository ppa:jonathonf/python-3.6 sudo apt-get update sudo apt-get install python ...

  7. linux下批量查找UTF-8的BOM文件,并去除BOM

    首先查找看看有哪些文件包含BOM find . -type f -print0 | xargs -0r awk '/^\xEF\xBB\xBF/ {print FILENAME} {nextfile} ...

  8. js调试工具及微博登录分析

    js调试工具 网页 都是采用ajax  需要提交一些参数 断点 自己去直接断点 事件断点, network 方法 靠近边栏顶部的是Call Stack(调用堆栈)窗格.当代码在断点处暂停时,Call ...

  9. java web开发环境tomcat安装配置

    1.下载jdk8并安装 2.下载tomcat windows环境下的免安装版zip包 3.设置两个环境变量 4.在tomcat的bin路径下双击startup.bat 启动tomcat服务器 5.使用 ...

  10. 远程连接mysql8.0,Error No.2058 Plugin caching_sha2_password could not be loaded

    通过本地去连接远程的mysql时报错,原因时mysql8.0的加密方法变了. mysql8.0默认采用caching_sha2_password的加密方式 第三方客户端基本都不支持这种加密方式,只有自 ...