字符处理编程笔记一
1.最长回文子串
1.最长回文子串
题目:
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
1
2
3
示例 2:
输入: "cbbd"
输出: "bb"
1
2
解法一:暴力解法
1.两重循环遍历所有子串;
2.判断回文子串。
时间复杂度:O(n3) O(n^3)O(n
3
)
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
string str;
getline(cin, str);
int max = 0;
int start = 0;
for (int i = 0; i < str.size(); i++)
{
for (int j = i; j < str.size(); j++)
{
int flag = 1;
for (int k = 0; k < (j - i + 1) / 2; k++)
{
if (str[k + i] != str[j - k])
{
flag = 0;
break;
}
}
if (flag && j - i + 1 > max)
{
max = j - i + 1;
start = i;
}
}
}
cout << str.substr(start, max) << endl;
return 0;
}
解法二:中心扩展法
1.遍历一遍字符串,将字符作为子串中心;
2.从子串中心从左右同时遍历,判断是否相同。
(奇子串n个和偶子串n-1个,二者区别在于需考虑是否用中心的字符)
时间复杂度:O(n2) O(n^2)O(n
2
)
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int expand(string s, int left, int right)
{
int l = left;
int r = right;
while (l >= 0 && r < s.size()&&s[l] && s[r] && s[l] == s[r])
{
l--;
r++;
}
return r - l - 1;
}
string findLongestPalindrome(string s)
{
if (s.empty() || s.size() < 1)
return s;
int start = 0;
int end = 0;
int len = 0;
for (int i = 0; i < s.size(); i++)
{
int len1 = expand(s, i, i);
int len2 = expand(s, i, i + 1);
len = max(len1, len2);
if (len > end - start)
{
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substr(start, end - start + 1);
}
int main() {
string str;
getline(cin, str);
//中心扩展法
cout << findLongestPalindrome(str) << endl;
return 0;
}
解法三:动态规划
状态转移方程:
dp[j][i]=⎧⎩⎨true,str[j]==str[i],str[j]==str[i]&&dp[j+1][i−1],j=i;j−i<2j−1>=2 dp[j][i]=\left\{\begin{aligned}&true ,&j=i;\\&str[j]==str[i] ,&j-i < 2\\&str[j]==str[i]\&\&dp[j+1][i-1],&j-1 >=2\end{aligned}\right.dp[j][i]=
⎩
⎪
⎨
⎪
⎧
true,
str[j]==str[i],
str[j]==str[i]&&dp[j+1][i−1],
j=i;
j−i<2
j−1>=2
时间复杂度:O(n2) O(n^2)O(n
2
)
string longestPalindrome(string s) {
const int n=s.size();
bool dp[n][n];
fill_n(&dp[0][0],n*n,false);
int max_len=1; //保存最长回文子串长度
int start=0;//保存最长回文子串起点
for(int i=0;i<s.size();++i)
{
for(int j=0;j<=i;++j)
{
if(i-j<2)
dp[j][i]=(s[i]==s[j]);
else
dp[j][i]=(s[i]==s[j] && dp[j+1][i-1]);
if(dp[j][i] && max_len<(i-j+1))
{
max_len=i-j+1;
start=j;
}
}
}
return s.substr(start,max_len);
}
解法四:马拉车算法(Manacher)
1.扩展字符串均为奇回文,避免奇回文和偶回文两次处理;
2.建立数组,存储以str[i]为中心的最长回文半径;
3.确定最长回文半径和最长回文子串长度之间的关系: maxLen=p[i]−1 maxLen = p[i] - 1maxLen=p[i]−1;
4.首位添加两个特殊符号,最长回文子串的起始索引index=(i−p[i])÷2 index = (i - p[i])\div 2index=(i−p[i])÷2
5.设置两个辅助变量:
mx: mx:mx:表示当前能延伸到最右端的回文子串的最右端点;
id: id:id:表示当前能延伸到最右端的回文子串的中心端点。
p[i]=mx>i?min(p[2×id−i],mx−i):1 p[i] = mx > i ? min(p[2 \times id - i], mx - i) : 1p[i]=mx>i?min(p[2×id−i],mx−i):1
由于回文子串中心对称,当p[i]+i<mx p[i]+i <mxp[i]+i<mx时,p[i]==p[2×id−i] p[i] ==p[2 \times id - i]p[i]==p[2×id−i],否则,p[i]==mx−i p[i] == mx - ip[i]==mx−i,而超过mx的部分需要继续遍历得到。
时间复杂度:O(n) O(n)O(n)
string rebuildStr(string &s)
{
string newStr(s.length() * 2 + 2,' ');
int k = 0;
newStr[k++] = '@';
for (int i = 0; i < s.length(); i++)
{
newStr[k++] = '#';
newStr[k++] = s[i];
}
newStr[k++] = '#';
newStr[k] = '*';
return newStr;
}
int manacher(string &s)
{
string str = rebuildStr(s);
int len = str.size();
vector<int> p(len, 0);
int mx = 0,id = 0;//mx为最右边,id为中心点
//最长回文子串的长度和索引
int maxLen = 0;
int index = 0;
for (int i = 1; i < str.size(); i++) {
p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;
while (str[i + p[i]] == str[i - p[i]]) p[i]++;//向左右扩展
//更新mx
if (p[i] + i > mx) {
mx = p[i] + i;
id = i;
}
if (maxLen < p[i] - 1)
{
maxLen = p[i] - 1;
index = i;
}
}
int start = (index - maxLen) / 2;
string subStr = s.substr(start, maxLen);
return maxLen;
}
————————————————
版权声明:本文为CSDN博主「AraSauHan」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_42018838/article/details/103929222