leetcode题目 寻找最长回文字串
2015-10-02 21:16
344 查看
题目: Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
思路: 常见的解法有三种,时间复杂度分别为O(n3)、O(n2)、O(n)。本文分别实现这三类算法,贴出代码。
解法一: 暴力法,遍历所有可能的字串,判断是否为回文,若是回文记下最长字串长度。以下代码还做了些许优化,已知长度为len的回文字串,则长度小于len的字串直接跳过不做判断。
本地测试结果,有两个测试用例,输出均正确
将以上代码贴到leetcode上,结果却是时间超限。给出的测试用例可以看出很长,O(n3)的解法果然不行。
解法二: 遍历字串,从某个中心开始向两边扩展寻找最长长度的回文串,此解法时间复杂度为O(n2)。注意要区分回文串为奇数长度和偶数长度的情况。另外为了方便边界控制,在向两边延伸时分别使用了string::iterator和string::reverse_iterator.这样向前和向后都可以通过判断是否小于s.rend或者小于s.end来作为边界条件。否则若使用s.begin和s.end作为边界条件则稍复杂。
此种解法通过了leetcode所有用例,但是时间效率却不高,只击败了17%的代码,很明显还有更好的解法。
解法3: Manacher算法,时间复杂度O(n).具体思路介绍网上有很多博客,本文代码则参考了左程云的书籍《IT名企算法与数据结构题目最优解》中关于Manacher算法的介绍。
提交的leetcode后果然速度快了很多,击败了77%的代码。
思路: 常见的解法有三种,时间复杂度分别为O(n3)、O(n2)、O(n)。本文分别实现这三类算法,贴出代码。
解法一: 暴力法,遍历所有可能的字串,判断是否为回文,若是回文记下最长字串长度。以下代码还做了些许优化,已知长度为len的回文字串,则长度小于len的字串直接跳过不做判断。
#include<iostream> #include<string> #include<vector> #include <stdlib.h> #include <map> using namespace std; class Solution { public: string longestPalindrome(string s) { int maxlen=0; auto start = s.begin(); //长度小于maxlen的字串直接跳过 for (auto i = s.begin(); i != s.end()&&s.end()-i>maxlen; ++i) { int len = 0; for (auto j = s.end(); j > i&&(j-i>maxlen); --j) { //若是回文则记下长度,因为之前已经判断当前字串长度大于maxlen if (IsPalindromic(i, j)) { maxlen = j - i; start = i; } } } return string(start,start+maxlen); } bool IsPalindromic(string::iterator begin,string::iterator end) { if (end <= begin) return false; --end; while (begin < end &&*begin==*end) { begin++; end--; } if (begin >= end) return true; else return false; } }; int main() { string a; Solution s; while (cin >> a) cout << s.longestPalindrome(a); }
本地测试结果,有两个测试用例,输出均正确
将以上代码贴到leetcode上,结果却是时间超限。给出的测试用例可以看出很长,O(n3)的解法果然不行。
解法二: 遍历字串,从某个中心开始向两边扩展寻找最长长度的回文串,此解法时间复杂度为O(n2)。注意要区分回文串为奇数长度和偶数长度的情况。另外为了方便边界控制,在向两边延伸时分别使用了string::iterator和string::reverse_iterator.这样向前和向后都可以通过判断是否小于s.rend或者小于s.end来作为边界条件。否则若使用s.begin和s.end作为边界条件则稍复杂。
#include<iostream> #include<string> #include<vector> #include <stdlib.h> #include <map> using namespace std; class Solution { public: string longestPalindrome(string s) { int maxlen = 0; int lenodd = 0; int leneven = 0; int len=0; auto start = s.begin(); for (auto i = s.begin(); i != s.end(); ++i) { //偶数长度的回文串最长半径 leneven = IsPalindromicEven(i, s.end(), s.rend()); //奇数长度的回文串最长半径 lenodd = IsPalindromicOdd(i, s.end(), s.rend()); if (2 * leneven > maxlen) { start = i + 1 - leneven; maxlen = 2 * leneven; } if (2 * lenodd-1 > maxlen) { start = i + 1 - lenodd; maxlen = 2 * lenodd - 1; } } return string(start, start + maxlen); } int IsPalindromicEven(string::iterator start, string::iterator end, string::reverse_iterator rend) { //这里fir指向start,sec指向start后边一位 string::reverse_iterator fir = static_cast<string::reverse_iterator> (start+1); string::iterator sec = start+1; int len = 0; while (fir <rend &&sec < end&&*fir == *sec) { ++len; ++fir; ++sec; } return len; } int IsPalindromicOdd(string::iterator start, string::iterator end,string::reverse_iterator rend ) { //这里fir赋值时需要加1,反向迭代器才能和正向迭代器指向同一个字符 string::reverse_iterator fir = static_cast<string::reverse_iterator> (start + 1); string::iterator sec = start; int len = 0; while (fir <rend &&sec < end&&*fir == *sec) { ++len; ++fir; ++sec; } return len; } }; int main() { string a; Solution s; while (cin >> a) cout << s.longestPalindrome(a); }
此种解法通过了leetcode所有用例,但是时间效率却不高,只击败了17%的代码,很明显还有更好的解法。
解法3: Manacher算法,时间复杂度O(n).具体思路介绍网上有很多博客,本文代码则参考了左程云的书籍《IT名企算法与数据结构题目最优解》中关于Manacher算法的介绍。
class Solution { public: string longestPalindrome(string s) { if (s.size() == 0) return string(); string temp; temp.push_back('#'); for (auto i = s.begin(); i != s.end(); ++i) { temp.push_back(*i); temp.push_back('#'); } int *pArr = (int *) malloc(4*temp.size()); memset(pArr, 0, 4*temp.size()); int index = -1; int pr = -1; int maxlen = 0; int start = 0; for (int i = 0; i != temp.size(); ++i) { pArr[i] = pr>i ? min(pArr[2 * index - i], pr - i):1; while (i + pArr[i]<temp.size() && i - pArr[i]>-1) { auto a = temp[i - pArr[i]]; a = temp[i - pArr[i]]; if (temp[i + pArr[i]] == temp[i - pArr[i]]) pArr[i]++; else break; } if (i + pArr[i] > pr) { pr = i + pArr[i]; index = i; } if (pArr[i] > maxlen) { start = i; maxlen = max(maxlen, pArr[i]); } } start /= 2; return string(s, start - ( maxlen - 1) / 2, maxlen-1); } };
提交的leetcode后果然速度快了很多,击败了77%的代码。
相关文章推荐
- C# 反射
- RabbitMQ OS X下安装及常用命令-1
- 深入Java虚拟机笔记---ClassLoader
- usaco Letter Game
- BZOJ4297 : [PA2015]Rozstaw szyn
- LeetCode题解:Sum Root to Leaf Numbers
- 【译】velocity
- 关于StdAfx.h和StdAfx.cpp
- BZOJ4295 : [PA2015]Hazard
- 将链串s中的所有子串"abc"删除
- Java中的null到底是什么?
- C++控制台下的贪吃蛇
- LeetCode题解:Word Ladder
- [LeetCode]: 35: Search Insert Position
- matlab中使用elseif和if嵌套的对比
- hdu 5490 Simple Matrix(数论)
- OC 打印结构体的内容
- 逻辑操作符---Lua: and,or,not 对比 C++:&&,||,!
- Spring 之AOP AspectJ切入点语法详解(最全了,不需要再去其他地找了)
- 有用的大牛站点