#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
说明
元音回文串可以只包含辅音,此时没有任何限制。