今天某面试题,要在任意给定的字符串中,寻找最长回文串的长度。
注:回文串是指abc、abbc、aaabccc、aaaccc这种左右对称的字符串。
比如:abcaabbbaad字符串中,最长回文串的长度是7。
/**
* 思路:<br>
* 动态规划。一个指针从头往后遍历;一个指针从后往前遍历,把之前比较过的结果存到二维数组内,每个单元表示截止匹配到某一个字符串,所属的最长子串长度。<br>
* 因为从前往后和从后往前遍历是同一个串,所以这个二维数组是轴对称的。<br>
* 关键是这一步:当某时刻匹配上时,statArr[i][j] = statArr[j][i] = statArr[i - 1][j + 1] + 1,后一次的结果为前一次的结果加一。
*
* @author Ray Chase
*
*/
public final class SymmetricalSearch {
/**
* default constructor
*/
private SymmetricalSearch() {
}
/**
* 寻找最长的对称字符串长度
*
* @param word
* 被查字串
* @return 最长的字符串片段长度
*/
public static int getLongestSymmetricalSection(char[] word) {
// guard condition
if (null == word || 0 == word.length)
return 0;
// 存放状态的数组,取值表示最近一次匹配的字串长度
int[][] statArr = new int[word.length][word.length];
// 最长的字串的长度
int max = 0;
// 最近一行是否匹配上
boolean matched;
// i表示word自左向右方向遍历的指针,在动态规划的map中表示行号
for (int i = 0; i < word.length; i++) {
// 最近一行是否匹配上,每行开头预置位false
matched = false;
// j表示word自右向左方向遍历的指针,在动态规划的map中表示列号
for (int j = word.length - 1; j >= 0; j--) {
// 如果在边界,匹配上了
if (0 == i || word.length - 1 == j) {
if (word[i] == word[j]) {
matched = true;
statArr[i][j] = statArr[j][i] = 1;
}
}
// 不在边界且匹配上了,最近一次匹配的长度为上次匹配的长度+1
else if (word[i] == word[j]) {
matched = true;
statArr[i][j] = statArr[j][i] = statArr[i - 1][j + 1] + 1;
// 更新最长字串长度和字串起始位置
if (statArr[i][j] > max) {
max = statArr[i][j];
}
}
}
// 如果本行没有匹配上且当前发现的最大匹配长度已经大于等于剩下可能匹配的最大长度,没有必要继续了
if (!matched && max >= word.length - (i + 1)) {
break;
}
}
//不存在长度为1的对称字串
return 1 == max ? 0 : max;
}
}
这个算法其实还有改进的空间,譬如,动态规划不需要N平方的空间,因为每次使用这个statArr的时候,只用一行的数据,因此空间复杂度可以由n平方变为n。
具体的办法,还有其他改进的办法,欢迎大家讨论。:)
附,测试用例:
public class SynmetricalSearchTest extends TestCase {
public void testAbnormal() {
assertEquals(0, SymmetricalSearch.getLongestSymmetricalSection(null));
assertEquals(0, SymmetricalSearch.getLongestSymmetricalSection(""
.toCharArray()));
}
public void testOdd() {
assertEquals(5, SymmetricalSearch.getLongestSymmetricalSection("12321"
.toCharArray()));
assertEquals(3, SymmetricalSearch.getLongestSymmetricalSection("121"
.toCharArray()));
}
public void testEven() {
assertEquals(4, SymmetricalSearch.getLongestSymmetricalSection("1221"
.toCharArray()));
assertEquals(6, SymmetricalSearch.getLongestSymmetricalSection("123321"
.toCharArray()));
}
public void testMixOdd() {
assertEquals(5, SymmetricalSearch
.getLongestSymmetricalSection("asf12321drf".toCharArray()));
assertEquals(3, SymmetricalSearch.getLongestSymmetricalSection("121"
.toCharArray()));
}
public void testMixEven() {
assertEquals(4, SymmetricalSearch
.getLongestSymmetricalSection("6781221787".toCharArray()));
assertEquals(6, SymmetricalSearch
.getLongestSymmetricalSection("55512332144".toCharArray()));
}
public void testMixNone() {
assertEquals(0, SymmetricalSearch.getLongestSymmetricalSection("6784"
.toCharArray()));
}
public void testNesting() {
assertEquals(7, SymmetricalSearch
.getLongestSymmetricalSection("3332333".toCharArray()));
assertEquals(6, SymmetricalSearch
.getLongestSymmetricalSection("af323323ed".toCharArray()));
}
}
---------------------------------------------------------------------------------------------------------------------------------
2012-3-26 更新:
同事提点我,这个做法是有问题的,如果出现这样的字符串,比如:abcdba,这个做法会认为ab是最大子串,那就错了,因此,这个算法在每次发现疑似最大回文串时都要进行校验,当然,这样效率就低了不少。
网上比较多的相对比较简单的正确解法是后缀树解法,这里有一位高人的blog。
不过,小罗同学说,甚至可以把复杂度降到O(n)!太不可思议了是不是?据说这叫Manacher算法,看这里和这里。
文章系本人原创,转载请注明出处和作者
分享到:
相关推荐
此外,值得注意的是,该问题还有其他解法,如动态规划、Manacher's Algorithm等,它们各有优缺点,适用于不同的场景。Manacher's Algorithm尤其在处理长字符串时效率较高,因为它能够在线性时间内找到最长回文子串。...
Bzoj2342要求寻找字符串中长度为4的倍数的子串,该子串满足以下条件:整个子串本身是回文串,且将其等分为两部分后,每一部分也是回文串。 ##### 2. 解决方案 - **后缀数组**:构建后缀数组,并计算height数组。 - ...
在本压缩包中,主题聚焦于C语言编程基础与LeetCode算法题目的解法,特别是针对第五题“最长回文子串”的问题。这是一道经典的字符串处理问题,旨在锻炼和提升C语言程序员的逻辑思维和算法实现能力。在本文中,我们将...
回文串是指一个字符串,无论是从左向右读还是从右向左读,其顺序都是一样的。本文将探讨如何高效地解决这类问题。 在LeetCode等在线编程平台上,你可以通过类别目录找到“回文串问题”的题目,并根据推荐指数和难度...
主函数`longestPalindrome`通过两个循环来寻找奇数和偶数长度的最长回文子串。 **面试相关性:** 在Java求职面试中,这道题目考察了应聘者的算法理解能力、问题解决能力和编程技巧。此外,还涉及到字符串处理、动态...
对于最长回文子串,我们可以建立一个二维数组dp,其中dp[i][j]表示字符串从索引i到j的子串是否为回文串。 1. 初始化:对于长度为1的子串,肯定是回文串,所以dp[i][i] = true。 2. 状态转移:对于长度大于1的子串,...
如果两者相等且当前回文串长度大于已找到的最长回文串,更新最长回文串并继续递归,分别向中心两侧扩展。 在`longestPalindrome`函数中,首先处理特殊情况:如果字符串长度小于等于1,则直接返回字符串本身,因为...
5. 最长回文子串:要求找出字符串中的最长回文子串。 针对以上问题,动态规划可以使用“状态”和“状态转移方程”来描述问题的解决过程。状态是指某个问题的子问题的解的集合,而状态转移方程描述了状态之间的转移...
其中,第 5 题 "最长回文子串" 是一个经典的字符串处理问题。回文串是指一个字符串从前往后读和从后往前读完全相同的字符串,例如 "madam" 或 "level"。 本题目的目标是找到给定字符串 `s` 中的最长回文子串。题目...
在ACM(国际大学生程序设计竞赛)中,字符串处理是一个重要的主题,涉及到各种算法和技巧。这份文档"ACM字符串题目及源代码[参照].pdf"涵盖了多个与字符串相关的题目和解决方案,主要集中在寻找最长公共子串、最长重复...
动态规划求最长上升子序列**:这是一个经典的动态规划问题,旨在寻找给定序列中最长的递增子序列,通常使用dp数组来记录每一步的最优解。 #### 二、回溯算法 **回溯算法**是一种通过尝试解决问题的不同可能性来...
找到一个字符串中最长的回文子串,可以用动态规划解决。创建一个二维状态表,记录每个子串是否为回文,最后根据状态表找出最长回文子串。 1.6 字符串的全排列 字符串的全排列问题属于组合问题,可以使用回溯法或者...
构成回文序列最少要增加多少字符 方法一: 为递归比较数组的头和尾: 如果头尾对应相同,则回文序列求解...需要增加数目为字符串总数减去最长公共子串长度。 http://blog.csdn.net/ssuchange/article/details/17385039
该算法利用了回文串的对称性,将时间复杂度降低到了O(N),其中N是字符串的长度。在传统的暴力解法中,我们需要以每个字符为中心向两边扩展,时间复杂度为O(N^2)。Manacher算法通过巧妙的数据结构和循环处理,避免了...
要注意一个细节:回文串的长度可能是奇数,也可能是偶数。 我们完全可以设计一个方法,兼容以上两种情况: 1、如果传入重合的索引编码,进行中心扩散,此时得到的最长回文子串的长度是奇数; 2、如果传入相邻的索引...
在原始问题中,我们需要找到一个字符串中,以某个字符为中心的最长回文子串。通过维护回文半径数组pArr和最右回文右边界R,以及对应的回文中心C,Manacher算法能够在遍历字符串的过程中快速更新这些信息。对于回文...
4. **最长回文子串** 可以采用动态规划或中心扩散法求解,动态规划的解决方案会使用二维数组记录子串是否为回文,中心扩散法则从每个可能的中心位置开始向两边扩展,找到最长的回文子串。 5. **Z 字形变换** 这个...
2. **最长回文串[M]**:给定一个包含大写和小写英文字母的字符串,找出其中最长的回文子串。一种有效的解法是使用中心扩展法。 #### 四、链表 **链表**是一种常见的数据结构,涉及到各种链表的操作。 1. **两个...
- **回文串问题**:包括判断一个字符串是否为回文,以及寻找最长回文子串,如`valid-palindrome`和`longest-palindromic-substring`。 2. **数组知识** - **数组类算法**:包括数组的基本操作,如查找、插入、...
本题的解题思路是先构造出一个序列,然后用动态规划算法处理出以中心的最长回文串的长度。由于是回文串,所以对于, 也就是回文串,显然这样包含了所有以为中心的回文串,也即以为开头的回文串数量都可以用差分来...