#P2183. 2024.10.13-第4题-最长元音回文字串
-
1000ms
Tried: 81
Accepted: 15
Difficulty: 6
所属公司 :
字节
时间 :2024年10月13日
-
算法标签>马拉车算法
2024.10.13-第4题-最长元音回文字串
因为辅音对回文没有影响,所以直接全部替换成相同字母,问题转化成了给定一个字符串求最长回文子串,直接Manacher即可
c++
#include <bits/stdc++.h>
using namespace std;
char s[100005]; // 原始字符串
char t[300005]; // 转换后的字符串,包含分隔符
int p[200005]; // 存储每个字符为中心的回文半径
int main() {
int n;
cin >> n;
scanf("%s", s);
int x = 0, y = 0;
// 将字符串 s 转换为 t,分隔元音和辅音
while (x < n) {
t[y++] = '|'; // 在 t 中插入分隔符 '|'
if (s[x] == 'a' || s[x] == 'e' || s[x] == 'i' || s[x] == 'o' || s[x] == 'u')
t[y++] = s[x++]; // 如果是元音,直接插入
else
t[y++] = 'w', x++; // 如果是辅音,插入 'w'
}
t[y++] = '|';
n = n * 2 + 1;
p[0] = 0; // 初始化回文半径数组的第一个元素
int ans = 0, r = 0, c = 0; // ans: 最大回文半径, r: 右边界, c: 中心位置
// 使用马拉车算法计算每个字符为中心的回文半径
for (int i = 1; i < n; i++) {
if (i < r) {
int j = c - (i - c);
p[i] = min(p[j], r - i);
} else p[i] = 0;
int le = i - p[i] - 1, ri = i + p[i] + 1;
while (le >= 0 && ri < n && t[le] == t[ri]) {
le--; ri++;
p[i]++;
}
ri = i + p[i];
if (ri > r) r = ri, c = i;
if (ans < p[i]) ans = p[i];
}
cout << ans << endl;
}
python
def main():
n = int(input())
s = input().strip()
t = []
x, y = 0, 0
# 将字符串 s 转换为 t,分隔元音和辅音
while x < n:
t.append('|')
if s[x] in 'aeiou':
t.append(s[x])
x += 1
else:
t.append('w')
x += 1
t.append('|')
n = len(t)
p = [0] * n
ans = 0
r = 0
c = 0
# 使用马拉车算法计算每个字符为中心的回文半径
for i in range(1, n):
if i < r:
j = c - (i - c)
p[i] = min(p[j], r - i)
else:
p[i] = 0
# 扩展回文
le = i - p[i] - 1 # 左边界
ri = i + p[i] + 1 # 右边界
while le >= 0 and ri < n and t[le] == t[ri]:
le -= 1
ri += 1
p[i] += 1
ri = i + p[i]
if ri > r:
r = ri
c = i
if ans < p[i]: # 更新最大回文半径
ans = p[i]
print(ans) # 输出结果
if __name__ == "__main__":
main() # 调用主函数
java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); // 创建扫描器用于输入
int n = scanner.nextInt(); // 读取字符串的长度
String s = scanner.next(); // 读取字符串 s
char[] t = new char[300005]; // 用于存储转换后的字符串,包含分隔符
int[] p = new int[200005]; // 存储每个字符为中心的回文半径
int x = 0, y = 0; // x 用于遍历 s,y 用于构建 t
// 将字符串 s 转换为 t,分隔元音和辅音
while (x < n) {
t[y++] = '|'; // 在 t 中插入分隔符 '|'
// 检查当前字符是否是元音
if (s.charAt(x) == 'a' || s.charAt(x) == 'e' || s.charAt(x) == 'i' ||
s.charAt(x) == 'o' || s.charAt(x) == 'u') {
t[y++] = s.charAt(x++); // 如果是元音,直接插入
} else {
t[y++] = 'w'; // 如果是辅音,插入 'w'
x++; // 移动到下一个字符
}
}
t[y++] = '|'; // 在 t 的末尾插入分隔符 '|'
n = n * 2 + 1; // 更新 n 的值为 t 的长度
p[0] = 0; // 初始化回文半径数组的第一个元素
int ans = 0; // 记录最大回文半径
int r = 0; // 当前回文的右边界
int c = 0; // 当前回文的中心位置
// 使用马拉车算法计算每个字符为中心的回文半径
for (int i = 1; i < n; i++) {
if (i < r) { // 如果 i 在当前回文的右边界内
int j = c - (i - c); // 找到对称位置 j
p[i] = Math.min(p[j], r - i); // 计算 p[i] 的初始值
} else {
p[i] = 0; // 否则初始化为 0
}
// 扩展回文
int le = i - p[i] - 1; // 左边界
int ri = i + p[i] + 1; // 右边界
// 检查左右字符是否相等,并扩展回文
while (le >= 0 && ri < n && t[le] == t[ri]) {
le--; // 向左扩展
ri++; // 向右扩展
p[i]++; // 增加回文半径
}
ri = i + p[i]; // 更新右边界
if (ri > r) { // 如果新的右边界大于当前右边界
r = ri; // 更新右边界
c = i; // 更新中心位置
}
if (ans < p[i]) { // 更新最大回文半径
ans = p[i];
}
}
System.out.println(ans); // 输出结果
scanner.close(); // 关闭扫描器
}
}
题目内容
小红有一个长度为n的字符串s,字符串仅包含小写英文字符。 定义元音字母为a,e,i,o,u,其他字母为辅音字母。 请你在s中找到一个最长的元音回文子串,只需要输出其长度。 子串是指从原字符串中,选择一段连续的字符组成的新字符串。 一个长度为m的元音回文串t是指,对于任意i∈[1,m],如果t是元音,则需满足;如果是辅音,那么没有额外限制。
输入描述
第一行输入一个整数n(1≤n≤105),代表字符串的长度。 第二行输入一个长度为n且仅由小写英文字母组成的字符串s
输出描述
在一行上输出一个整数,代表最长的元音回文串的长度。
样例1
输入
5
abaeb
输出
3
说明
对于区间[1,3],s1和s3位置对称,并且字符相等,符合题意。 对于区间[1,4],s2和s3位置对称,至少有一个元音,但是字符不相等,不符合题意。 对于区间[2,5],s3和s4位置对称,至少有一个元音,但是字符不相等,不符合题意。
样例2
输入
6
cccccc
输出
6
说明
元音回文串可以只包含辅音,此时没有任何限制。