首页 > 代码库 > 最长回文子串的不同解法
最长回文子串的不同解法
给定一个字符串,返回该字符串的最长回文子串,回文也就是说 ,正着读和反着读是一样的。下面总结了几种求回文的方式:
方法1 : 很简单,枚举所有的区间 [i,j] ,查看该范围内是否是一个回文.
时间复杂度 O(n^3),空间复杂度 O(1).
方法2: 方法1的时间复杂度太高,并且存在着大量的重复运算,可以使用DP来解,并且保存已经检查过的字符串的状态.
时间复杂度: O(n^2),空间复杂度O(n^2).
这里存在两种DP的方法,是根据区间来进行DP,还是长度,不过都是大同小异,不改变整个算法的时间复杂度。
代码如下:
//dp 1 string LongestPalindrome(const string &s) { const int n = s.size(); if(n < 2) return s; bool f[n][n+1]; fill_n(&f[0][0],n*(n+1),false); int start = 0, len = 1; f[0][0] = true; for(int i=0;i<n;++i) { f[i][0] = true; f[i][1] = true; } for(int i=n-2;i>=0;--i) { for(int j=2;j<=n && (i+j-1)<n;++j) { f[i][j] = f[i+1][j-2] && s[i] == s[i+j-1]; if(f[i][j] && j > len) {start = i; len = j;} } } return s.substr(start,len); } //dp 2 string LongestPalindrome_dp2(const string &s) { const int n = s.size(); if(n < 2) return s; bool f[n][n]; fill_n(&f[0][0],n*n,false); int start=0,len=1; f[0][0] = true; for(int i=0;i<n;++i) f[i][i] = true; for(int i = n-1 ; i >= 0; --i) { for(int j = i+1; j < n;++j) { if(j == i+1) f[i][j] = (s[i] == s[j]); else f[i][j] = f[i+1][j-1] && s[i] == s[j]; if(f[i][j] && (j-i+1) > len) {start = i; len = j-i+1;} } } return s.substr(start,len); }
方法3: 很直观的想法,以每一个字符串为中心,计算该字符串左右可以延伸的部分。注意处理长度为奇数和偶数的情况。
时间复杂度 : O(n^2) ,空间复杂度 : O(1)
//从中间往两端延伸(考虑奇数偶数的情况即可) string LongestPalindrome_extend(const string &s) { const int n = s.size(); if(n < 2) return s; int low,high; int start=0,len=1; for(int i=1;i<n;++i) { //even low = i-1; high = i; while(low>=0&&high<n&&s[low]==s[high]) { if(high-low+1>len) { start=low; len=high-low+1; } --low;++high; } //odd low = i-1; high = i+1; while(low>=0&&high<n&&s[low]==s[high]) { if(high-low+1 > len) { start = low; len=high-low+1; } --low;++high; } } return s.substr(start,len); }
方法3 :使用后缀数组的思想,将字符串s取s的逆,拼接在s的后面,也就是说 现在考察的字符串是 s#s‘,其中的#是额外的一个字符,s‘是s的逆串。求当前这个新拼接而成的字符串的后缀树组的最长公共前缀。
时间复杂度: O(n^2),空间复杂度 O(n^2)
//关于此方法还没想明白,暂不贴代码
方法4: manacher算法。参考 点击打开链接
时间复杂度: O(n),空间复杂度 O(n)
代码为:
//Manacher O(n) string Manacher(const string &str) { //add '#' string s = "$"; for(auto a : str) { s += '#'; s += a; } s += '#'; cout << s << endl; const int n = s.size(); vector<int> P(n,0); int right = -1, idx = -1; for(int i=1;i<n;++i) { P[i] = (right > i)? min(P[2*idx-i],right-i):1; while(s[i+P[i]] == s[i-P[i]])P[i]++; if(i+P[i]>right) { right = i + P[i]; idx = i; } } auto pos = max_element(P.begin(),P.end()); int len = *pos-1; string ret; int i = pos-P.begin(); //print ret += s[i]; cout << ret << endl; int k=1; while(len) { ret += s[i+k]; ret = s[i-k]+ret; cout << ret << endl; ++k; --len; } //trim # string ret2; for(auto a :ret) if(a!='#')ret2 += a; return ret2; }
上述代码均已验证正确,至于原理,全在代码中。
最长回文子串的不同解法
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。