【算法题解】密码截取

78 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情 

描述

Catcher是MCA国的情报员,他工作时发现敌国会用一些对称的密码进行通信,比如像这些ABBA,ABA,A,123321,但是他们有时会在开始或结束时加入一些无关的字符以防止别国破解。比如进行下列变化 ABBA->12ABBA,ABA->ABAKK,123321->51233214 。因为截获的串太长了,而且存在多种可能的情况(abaaab可看作是aba,或baaab的加密形式),Cathcer的工作量实在是太大了,他只能向电脑高手求助,你能帮Catcher找出最长的有效密码串吗?

数据范围:字符串长度满足:1≤n≤2500 

输入描述:

输入一个字符串(字符串的长度不超过2500)

输出描述:

返回有效密码串的最大长度

示例1

输入:

ABBA

输出:

4

示例2

输入:

ABBBA

输出:

5

示例3

输入:

12HHHHA

输出:

4

题目的主要信息:

  • 输入一个长度不超过2500的字符串
  • 找到其中最长的回文子串长度

方法一:中心扩散法

具体做法:

我们可以遍历每个字符串每个位置,都以其为回文子串的中心,或是奇数长度子串的中心(从两边开始找),或是偶数长度的子串中心(以它左边和它一起),分别向两边扩散,只要这个过程中两边字符是相等的就可以继续,直到边界或者不相等,这就是以这个为中心的回文子串的长度,我们比较每一个中心找到最长即可。

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

int main(){
    string s;
    while(cin >> s){
        int maxlen = 0;
        for(int i = 1; i < s.length(); i++){ //每个点都可以为中心
            //计算以i-1和i为中心的偶数长度的回文子串长度
            int low = i - 1, high = i;
            while(low >= 0 && high < s.length() && s[low] == s[high]){ //中心两边必须都相同
                low--;
                high++;
            }
            maxlen = max(maxlen, high - low - 1); 
            //计算以i为中心的奇数长度的回文子串长度
            low = i - 1, high = i + 1;
            while(low >= 0 && high < s.length() && s[low] == s[high]){ //中心两边必须都相同
                low--;
                high++;
            }
            maxlen = max(maxlen, high - low - 1);
        }
        cout << maxlen << endl;
    }
    return 0;
}

复杂度分析:

  • 时间复杂度:O(n2)O(n^2),外循环遍历每个点,内循环最坏情况下遍历半个字符串长度
  • 空间复杂度:O(1)O(1),无额外空间

方法二:动态规划

具体做法:

我们用二维数组构建子串回文情况,其中dp[j][i]=1表示从j到i满足回文子串的要求,则有转移方程:

1.png 第一种情况是看以这个位置为中心的奇数长度的回文子串,第二种情况是以这两个位置为中心的偶数长度回文子串,第三种情况是这是边界,看中间是否是回文子串。最后找到最大的iii与jjj的距离即可。

#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;

int main(){
    string s;
    while(cin >> s){
        int n = s.length();
        vector<vector<bool> > dp(n, vector<bool>(n, false)); //dp[j][i]=1表示从j到i是回文子串
        int maxlen = 1; //初始为1
        for(int i = 0; i < n; i++){
            for(int j = 0; j <= i; j++){
                if(i == j) //奇数长度子串
                    dp[j][i] = true;
                else if(i - j == 1) //偶数长度子串
                    dp[j][i] = (s[i] == s[j]);
                else
                    dp[j][i] = (s[i] == s[j] && dp[j + 1][i - 1]); //这两个字符相等,同时中间缩也要相等
                if(dp[j][i] && i - j + 1 > maxlen) //取最大
                    maxlen = i - j + 1;
            }
        }
        cout << maxlen << endl;
    }
    return 0;
}

复杂度分析:

  • 时间复杂度:O(n2)O(n^2),遍历二维数组
  • 空间复杂度:O(n2)O(n^2),辅助空间为二维数组