小牛快传

题目 小牛快传

image-1a3f6e0f

思路分析

将原始字符串先存好

因为要保留换行符 所以用getline (遇到换行停止) 一停止就手动添加一个'\n'

然后把字符串过滤出来(去除非字母和统一变小写)

为了后续还原 记录一下在原串的位置

然后就是对过滤后的字符串进行查找最大回文子串

使用中心扩展算法(本质双指针 不过前后、对撞指针都见了 这种向两边的倒是第一次见)

基本思想是将字符串中的每一个字符或字符对视为潜在的回文中心,然后向两边扩展,以检查最长的回文子串 考虑两种情况:

奇数长度的回文: 这时,回文中心是一个字符。例如,在字符串 "racecar" 中,中心是 "e",回文是整个字符串。

偶数长度的回文: 这时,回文中心是两个字符之间的空隙。例如,在字符串 "abba" 中,中心在两个 "b" 字符之间。

这样可以得到最大回文串的最左和最右

长度就是他们相减+1

把它们映射回原字符串的位置 从原始字符串中截取出来

就是要的结果了

代码实现

#include<bits/stdc++.h>

using namespace std;

typedef pair<int,int> PII;

vector<int> indexes; // 存储过滤后每个字符在原始字符串中的位置

string filter(const string& str) {

    string res;

    for (int i = 0; i < str.size(); ++i) {

        auto c = str[i];

        if (isalpha(c)) {

            res += tolower(c);

            indexes.push_back(i); // 记录当前字符在原始字符串中的位置

        }

    }

    return res;

}

// 寻找最大回文子串的辅助函数

PII findLongestPalindrome(const string& s, int left, int right) {

    while (left >= 0 && right < s.size() && s[left] == s[right]) {

        left--;

        right++;

    }

    return {left + 1, right - 1};

}

int main()

{

    string input, line;

    while (getline(cin, line)) {

        input += line + '\n'; // 保留换行符 getline的妙用

    }

    string filtered = filter(input);

    int start = 0, end = 0; // 最大回文子串在过滤后字符串中的位置

    /*

    中心扩展算法 找输入字符串中的最大回文子串。

    基本思想是将字符串中的每一个字符或字符对视为潜在的回文中心,然后向两边扩展,以检查最长的回文子串

    考虑两种情况:

        奇数长度的回文:

            这时,回文中心是一个字符。例如,在字符串 "racecar" 中,中心是 "e",回文是整个字符串。

        偶数长度的回文:

            这时,回文中心是两个字符之间的空隙。例如,在字符串 "abba" 中,中心在两个 "b" 字符之间。

    */

    //循环遍历字符串中的每个位置 i,对于每个位置,尝试找到以该位置为中心的最长回文子串。

    for (int i = 0; i < filtered.size(); ++i)

    {

        //尝试找到以位置 i 为中心的最长奇数长度回文子串。

        //传递相同的索引值 i 给 findLongestPalindrome 函数,意味着中心是单个字符。

        auto [left1, right1] = findLongestPalindrome(filtered, i, i);

        //尝试找到以位置 i 和 i + 1 之间的空隙为中心的最长偶数长度回文子串。

        //传递 i 和 i + 1 作为参数给 findLongestPalindrome 函数,意味着中心是两个字符之间的空隙。

        auto [left2, right2] = findLongestPalindrome(filtered, i, i + 1);

        //对于每次扩展得到的回文子串,通过比较其长度与当前已知的最长回文子串长度,更新最长回文子串的起始和结束位置。

        if (right1 - left1 > end - start) {

            start = left1;

            end = right1;

        }

        if (right2 - left2 > end - start) {

            start = left2;

            end = right2;

        }

    }

    // 使用记录的位置恢复原始回文子串

    string result;

    int originalStart = indexes[start]; // 映射回原始字符串的起始位置

    int originalEnd = indexes[end]; // 映射回原始字符串的结束位置

    result = input.substr(originalStart, originalEnd - originalStart + 1);

    cout << end - start + 1 << endl; // 输出最大回文子串的长度

    cout << result; // 输出最大回文子串

    return 0;

}

同类题型

视频讲解


⬅️ 字符串匹配 🏠 00-刷题理模型 ➡️ 解码