【字符串】回文串专题(中心扩展法、manacher马拉车算法)
数据量较大时只能用manacher算法,但一般面试不会考到这么深,Leetcode上的题中心扩展法就可以解决。
中心扩展法
枚举以每个字符为中心,向左右两边扩展的最长回文串长度
求最长回文串的长度
注意,回文串的长度只可能是奇数或者偶数
长度为奇数时,传入work(s, i, i)
,表示左右起点均为
长度为偶数时,传入work(s, i, i + 1)
,表示左起点为,右起点为
int work(string& s, int l, int r)
{ // 以给定位置为中心向左右扩展,返回最长回文串长度
int n = s.size();
while (l >= 0 && r < n && s[l] == s[r])
{
l--, r++;
}
// l,r停留在第一个不匹配的位置,实际的回文串是从l+1到r-1
// 所以回文串的长度是(r-1)-(l+1)+1 = r - l - 1
return r - l - 1;
}
5. 最长回文子串 - 力扣(LeetCode)
本题不需要求出最长回文串的右边界r
,但是这里为了更清晰写上了。
class Solution {
public:
int work(string &s, int l, int r)
{
int n = s.size();
while (l >= 0 && r < n && s[l] == s[r])
{
l--, r++;
}
return r - l - 1;
}
string longestPalindrome(string s) {
int n = s.size();
int l = 0, r = 0, max_len = 0;
for (int i = 0; i < n; i++)
{
int len1 = work(s, i, i);
int len2 = work(s, i, i + 1);
if (max_len < len1)
{
max_len = len1;
l = i - len1 / 2;
r = i + len1 / 2;
}
if (max_len < len2)
{
max_len = len2;
l = i - (len2 - 1) / 2;
r = i + len2 / 2;
}
}
return s.substr(l, max_len);
}
};
P1210 [USACO1.3] 最长的回文 Calf Flac
#include <iostream>
#include <vector>
using namespace std;
int work(string& s, int l, int r)
{ // 以给定位置为中心向左右扩展,返回最长回文串长度
int n = s.size();
while (l >= 0 && r < n && s[l] == s[r])
{
l--, r++;
}
// l,r停留在第一个不匹配的位置,实际的回文串是从l+1到r-1
// 所以回文串的长度是(r-1)-(l+1)+1 = r - l - 1
return r - l - 1;
}
void solve()
{
string s, line;
while (getline(cin, line))
{
s += line + '\n';
}
string new_s;
vector<int> pos;
for (int i = 0; i < s.size(); i++)
{
if (isalpha(s[i]))
{
new_s += tolower(s[i]);
pos.push_back(i);
}
}
int st = 0, ed = 0, max_len = 0;
for (int i = 0; i < new_s.size(); i++)
{
int len1 = work(new_s, i, i); // 奇数,左右起点都是i
int len2 = work(new_s, i, i + 1); // 偶数,左起点是i,右起点是i+1
if (len1 > max_len)
{
max_len = len1;
st = i - len1 / 2;
ed = i + len1 / 2;
}
if (len2 > max_len)
{
max_len = len2;
st = i - (len2 - 1) / 2;
ed = i + len2 / 2;
}
}
string res = s.substr(pos[st], pos[ed] - pos[st] + 1);
cout << max_len << '\n' << res;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T = 1;
while (T--) solve();
return 0;
}
求回文子串的个数
常用函数
int cal(string &s, int l, int r)//计算以[l,r]为中心的回文串的数量
{
int n = s.size(), cnt = 0; //cnt记录回文串的个数
while (l >= 0 && r < n && s[l] == s[r])
{
l--, r++;
cnt++;
}
return cnt;
}
647. 回文子串 - 力扣(LeetCode)
class Solution {
public:
int cal(string &s, int l, int r)//计算以[l,r]为中心的回文串的数量
{
int n = s.size(), cnt = 0; //cnt记录回文串的个数
while (l >= 0 && r < n && s[l] == s[r])
{
l--, r++;
cnt++;
}
return cnt;
}
int countSubstrings(string s) {
int n = s.size();
int res = 0;
for (int i = 0; i < n; i++)
{
res += cal(s, i, i);//奇数个字符,以i为中心
res += cal(s, i, i + 1);//偶数个字符,以i,i+1为中心
}
return res;
}
};
Manacher算法
学习资料
1.F05 Manacher(马拉车)
2.算法讲解103【扩展】 Manacher算法、扩展KMP
代码模板
string a; // 原串
string s; // 改造串
int d[N]; // 回文半径函数
int n; // 改造串长度(不含哨兵)
void get_d(string& s, int n)
{
d[1] = 1;
for (int i = 2, l, r = 1; i <= n; i++)
{ // r取0或1都可,因为第一轮循环if都不会执行
if (i <= r) d[i] = min(d[r - i + l], r - i + 1);
while (s[i - d[i]] == s[i + d[i]]) d[i]++;
if (i + d[i] - 1 > r) l = i - (d[i] - 1), r = i + d[i] - 1;
}
}
int init()
{
cin >> a;
n = a.size();
a = ' ' + a; // 字符串下标从1开始
s = "$#"; // 初始化改造串
for (int i = 1; i <= n; i++) s += a[i], s += '#';
n = s.size() - 1; // 改造串长度(不含哨兵)
get_d(s, n); // 求最长半径
int res = 0;
for (int i = 1; i <= n; i++) res = max(res, d[i]);
return res - 1;
}
P3805 【模板】manacher
string版本
#include <iostream>
using namespace std;
const int N = 2.2e7 + 10;
string a; // 原串
string s; // 改造串
int d[N]; // 回文半径函数
int n;
void get_d(string& s, int n)
{
d[1] = 1;
for (int i = 2, l, r = 1; i <= n; i++)
{ // r取0或1都可,因为第一轮循环if都不会执行
if (i <= r) d[i] = min(d[r - i + l], r - i + 1);
while (s[i - d[i]] == s[i + d[i]]) d[i]++;
if (i + d[i] - 1 > r) l = i - (d[i] - 1), r = i + d[i] - 1;
}
}
void init()
{
cin >> a;
n = a.size();
a = ' ' + a; // 字符串下标从1开始
s = "$#"; // 初始化改造串
for (int i = 1; i <= n; i++) s += a[i], s += '#';
n = s.size() - 1; // 改造串长度(不含哨兵)
}
void solve()
{
init(); // 预处理
get_d(s, n); // 求最长半径
int res = 0;
for (int i = 1; i <= n; i++) res = max(res, d[i]);
cout << res - 1;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T = 1;
while (T--) solve();
return 0;
}
char*
版本
#include <cstring>
#include <iostream>
using namespace std;
const int N = 2.2e7;
char a[N], s[N];
int d[N]; // 回文半径函数
void get_d(char* s, int n)
{
d[1] = 1;
for (int i = 2, l, r = 1; i <= n; i++)
{
if (i <= r) d[i] = min(d[r - i + l], r - i + 1);
while (s[i - d[i]] == s[i + d[i]]) d[i]++;
if (i + d[i] - 1 > r) l = i - d[i] + 1, r = i + d[i] - 1;
}
}
int main()
{
// 改造串
scanf("%s", a + 1);
int n = strlen(a + 1), k = 0;
s[0] = '$', s[++k] = '#';
for (int i = 1; i <= n; i++) s[++k] = a[i], s[++k] = '#';
n = k;
get_d(s, n); // 计算d函数
int ans = 0;
for (int i = 1; i <= n; i++) ans = max(ans, d[i]);
printf("%d\n", ans - 1);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具