首页 > 试题广场 >

密码截取

[编程题]密码截取
  • 热度指数:241502 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
Catcher是MCA国的情报员,他工作时发现敌国会用一些对称的密码进行通信,比如像这些ABBA,ABA,A,123321,但是他们有时会在开始或结束时加入一些无关的字符以防止别国破解。比如进行下列变化 ABBA->12ABBA,ABA->ABAKK,123321->51233214 。因为截获的串太长了,而且存在多种可能的情况(abaaab可看作是aba,或baaab的加密形式),Cathcer的工作量实在是太大了,他只能向电脑高手求助,你能帮Catcher找出最长的有效密码串吗?

数据范围:字符串长度满足

输入描述:

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



输出描述:

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

示例1

输入

ABBA

输出

4
示例2

输入

ABBBA

输出

5
示例3

输入

12HHHHA

输出

4
manacher~~~~~~
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int Manacher(string oriStr) {
	string newStr;
	int len = oriStr.size();
	for (int i = 0; i < len; i++) {//插入间隔符
		newStr += '#';
		newStr += oriStr[i];
	}
	newStr += '#';
	len = 2 * len + 1;             //新串长度,必为奇数
	int maxRight = 0;              //当前访问到的所有回文子串中,所能触及的最右一个字符的位置
	int pos = 0;                   //maxRight对应的回文子串对称轴的位置
	int*RL = new int[len];         //RL[i]记录以i为对称轴的最长回文子串半径长度(对称轴到最左或最右的距离)
	int maxLength = 0;             //记录最长回文子串长度
	for (int i = 0; i < len; i++) {
		if (i < maxRight) {        //分两种情况,i在maxRight左边和右边
			RL[i] = min(RL[2 * pos - i], maxRight - i);
		}
		else RL[i] = 1;
		while (i - RL[i]>=0 && RL[i] + i < len && newStr[i - RL[i]] == newStr[i + RL[i]])
			RL[i]++;               //以i为中心,在上步的基础上扩展,直至到达边界或左右字符不相等
		if (maxRight < RL[i] + i - 1) {//更新maxRight和pos
			maxRight = RL[i] + i - 1;
			pos = i;
		}
		maxLength = max(maxLength, RL[i] - 1);//对以i为中心的回文子串在原串总的长度即为RL[i] - 1
											  //证明:新串中回文子串长度为2*RL[i]-1,其中RL[i]个
											  //插入字符,则剩下的RL[i]-1个为原字符
	}
	return maxLength;
}

int main() {
	string str;
	while (cin >> str) {
		cout << Manacher(str) << endl;
	}
	return 0;
}


发表于 2017-07-28 11:25:14 回复(5)
用了一个比较笨的方法,对于每一个字符,看他是不是与下一个字符相等(BAAB型),或者看他的上一个字符与下一个字符是否相等(ABA型),然后往回找就可以了
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main(){
    string str;
    while(getline(cin,str)){
        int len = str.length();
        if(len<=1)
        {
            cout<<len<<endl;
            continue;
        }
        int num = 1;
        for(int i=1;i<len-1;i++)
        {
            if(str[i]==str[i+1])
            {//偶数对成
                int curnum = 0;
                int start = i;int end =i+1;
                while(start>=0 && end<=len && str[start] == str[end])
                {
                    start--;
                    end++;
                    curnum+=2;
                }
                if(curnum>num)
                    num = curnum;
            }
            if(str[i-1]==str[i+1])
            {//偶数对成
                int curnum = 1;
                int start = i-1;int end =i+1;
                while(start>=0 && end<=len && str[start] == str[end])
                {
                    start--;
                    end++;
                    curnum+=2;
                }
                if(curnum>num)
                    num = curnum;
            }
        }
        cout<<num<<endl;
    }
    return 0;
}

发表于 2016-08-03 13:29:54 回复(9)
#include<iostream>
#include<vector>
using namespace std;
int main(){
	string str;
    while(cin >> str){
        vector<int> vi;       
        int len = str.size();
        for(int i = 0; i < len-1; ++i){
            for(int j = len-1; j > i; --j){
				if(str[i] == str[j]){
                    int i1 = i, j1 = j;
                    while(str[i1] == str[j1]){
                        if(i1 == j1-1 || i1 == j1)
                             vi.push_back(j-i+1);
                        i1++;
                        j1--;
                    } 
                }
            }
        }
        int max = vi[0];
        for(int i = 1; i < vi.size(); ++i){
           if(vi[i] > max)
               max = vi[i];
        }
        cout << max << endl;
    }
    return 0;
}

发表于 2017-03-28 20:57:34 回复(0)
注意这是求最大回文子串(连续出现),而不是最大回文子序列(无需连续,子序列即可)
import java.util.Scanner;
/*
    * 动态规划算法
    * dp(i, j) 表示是否 s(i ... j) 能够形成一个回文字符串
    * 当 s(i) 等于 s(j) 并且  s(i+1 ... j-1) 是一个回文字符串时 dp(i, j) 的取值为 true
    * 当我们找到一个回文子字符串时,我们检查其是否为最长的回文字符串
*/
public class Main {
     public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            String s = sc.nextLine();
            String res = longestPalindrome(s);
            System.out.println(res.length()); 
        }
       
    }
    public static String longestPalindrome(String s) {
        // n表示字符串的长度
        int n = s.length();
        String res = null;
        // 定义一个dp数组
        boolean[][] dp = new boolean[n][n];
        // 外层循环控制从最后一个字符向第一个字符渐进
        for (int i = n - 1; i >= 0; i--) {
            // 内层循环控制
            for (int j = i; j < n; j++) {
                // dp数组元素
                dp[i][j] = s.charAt(i) == s.charAt(j) && (j - i < 3 || dp[i + 1][j - 1]);

                if (dp[i][j] && (res == null || j - i + 1 > res.length())) {
                    res = s.substring(i, j + 1);
                }
            }
        }
        return res;
    }
}


发表于 2020-09-01 21:47:57 回复(0)
while True:
    try:
        s=list(input())
        count=0
        if len(s)==2:
            if s[0]==s[1]:
                print(2)
            else:
                print(0)
        else:
            for i in range(1,len(s)-1):
                if s[i]==s[i+1]:
                    num=0#偶数成对
                    start=i
                    end=i+1
                    while start>=0 and end<=(len(s)-1) and s[start]==s[end]:
                            start-=1
                            end+=1
                            num+=2
                    if num>count:
                        count=num
                if s[i-1]==s[i+1]:
                    num=1#奇数成对
                    start=i-1
                    end=i+1
                    while start>=0 and end<=(len(s)-1) and s[start]==s[end]:
                        start-=1
                        end+=1
                        num=num+2
                    if num>count:
                        count=num
            print(count)
    except:
        break

发表于 2019-07-13 17:43:33 回复(0)
典型的求最长回文子串算法,有一个复杂度为O(n)的manacher算法,专门解决这个问题。
#include<iostream>
#define MAX 10000
using namespace std;

#define min(a,b) ((a)<(b)?(a):(b))
//返回最大回文子串长度,
//输入:原字符串数组指针
//返回值:最大回文子串
int manacher(char *p)
{
    char s[MAX << 1] = "$#";
    int v[MAX << 1] = { 0 };
    int c = 1;
    //初始化
    while (*p)
    {
        s[++c] = *p++;
        s[++c] = '#';
    }
    s[++c] = ' ';
    int re = 0, mx = 0, cn = 0;
    int i;
    for (i = 1; s[i] != ' ';i++)
    {
        if (mx > i)
        {
            v[i] = min(v[cn * 2 - 1], mx - i);
        }
        else
            v[i] = 1;
        while (s[i+v[i]] == s[i-v[i]])
        {
            ++v[i];
        }
        if (v[i] + i > mx)
        {
            mx = v[i] + i;
            cn = i;
        }
        if (v[i]>re)
        {
            re = v[i];
        }
    }
    return re - 1;
}
void test()
{
    char s[MAX];
    while(cin >> s)
        {
        int result;
    result = manacher(s);
    cout << result << endl;
    }
    
}
int main()
{
    test();
    return 0;
}
发表于 2016-06-20 09:24:35 回复(0)
/*
    https://ethsonliu.com/2018/04/manacher.html
    https://leetcode.wang/leetCode-5-Longest-Palindromic-Substring.html#

    左程云算法讲解视频
    https://www.bilibili.com/video/BV13g41157hK?p=13
    位置1:30分
 */
import java.util.Scanner;

public class Main {

    // 字符串预处理
    public static char[] manacherString(String str) {
        char[] chars = str.toCharArray();
        char[] res = new char[str.length() * 2 + 1];
        int index = 0;

        for (int i = 0; i < res.length; i++) {
            if (i % 2 == 0) {
                res[i] = '#';
            } else {
                res[i] = chars[index];
                index++;
            }
        }
        return res;
    }

    public static int manacher2(String str) {
        if (str == null || str.length() == 0) {
            return 0;
        }
        char[] s = manacherString(str);
        int[] p = new int[s.length];
        int C = 0;
        int R = 0;
        int maxLen = Integer.MIN_VALUE;

        for (int i = 0; i < s.length; i++) {
            int iMirror = 2 * C - i;
            if (i < R) {
                p[i] = Math.min(p[iMirror], R - i);
            } else {
                p[i] = 1;
            }
            while (i + p[i] < s.length && i - p[i] >= 0) {
                if (s[i + p[i]] == s[i - p[i]]) {
                    p[i]++;
                } else {
                    break;
                }
            }
            if (i + p[i] > R) {
                R = i + p[i];
                C = i;
            }
            maxLen = Math.max(maxLen, p[i]);
        }
        return  maxLen - 1;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            String str = sc.nextLine();
            System.out.println(manacher2(str));
        }
    }
}
刚理解这个算法,过几天再复习一遍,看能不能写出来
发表于 2021-11-02 16:00:37 回复(0)
如果着急刷题,暂时不想学马拉车算法的话,可以就用双重循环,没有规定时间复杂度为O(n)的话,直接算也无妨,当然,如果能用马拉车算法那更好了

直接循环有个问题,就是偶数最长回文子串长度不对,那么我们就引入两个索引,都算一遍,也就是时间复杂度为O(2*n^2)
看代码:
简而言之就是我们每一次都计算这个位置的数的最长回文长度和这个数跟下个数一起的最长回文长度。然后再进行比较就好啦。
import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        while(input.hasNext()){
            String s = input.nextLine();
            if(s == null || s.equals("")){
                break;
            }
            int result = 1;
            for(int i = 0;i<s.length();i++){
                int max1 = calMaxStr(s, s.split(""),i,i);
                int max2 = calMaxStr(s, s.split(""),i,i + 1);
                result = Math.max(max1, result);
                result = Math.max(max2, result);
            }
            System.out.println(result);
        }
    }
    
    // 计算最长回文子串的长度公共方法
    public static Integer calMaxStr(String s, String[] sArray,int l, int r){
        while(l >= 0 && r < sArray.length && sArray[l].equals(sArray[r])){
            l--;
            r++;
        }
        return r-l-1;
    }
}


发表于 2021-11-01 01:06:31 回复(0)
我这个还挺好理解的,也没用什么dp和mancher的算法,单纯暴力逻辑破解。
import java.util.Scanner;

/**
 * @author Yuliang.Lee
 * @version 1.0
 * @date 2021/9/9 10:40
 * 最长的对称字符串:
    比如像这些ABBA,ABA,A,123321,称为对称字符串,定义为有效密码。
    输入一个字符串,返回有效密码串的最大长度。
 * 示例:
    输入1:1kABBA21
    输出1:4
    输入2:abaaab
    输出2:5
 */
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (in.hasNext()) {
            String pswd = in.nextLine();
            int maxValid = 0;
            for (int n = pswd.length(); n > 0; n--) {
                if (hasValidWithinLen(pswd, n)) {
                    maxValid = n;
                    break;
                }
            }
            System.out.println(maxValid);
        }
    }

    // 判断是否存在长度为len的对称字符串
    public static boolean hasValidWithinLen(String password, int len) {
        for (int i = 0; i + len - 1 < password.length(); i++) {
            int left = i;
            int right = i + len - 1;
            while (left < right) {
                // 指针从最左侧最右侧的字符往中间对称轴靠,如果当中有任意一对左右字符不相等则结束本次对称判断
                if (password.charAt(left) != password.charAt(right)) {
                    break;
                }
                left++;
                right--;
            }
            if (left >= right) {
                return true;
            }
        }
        return false;
    }
}


发表于 2021-09-09 11:59:22 回复(0)
力扣原题,最长回文字符串,向两边扩展;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            String s = sc.next();
            int ans = 0;
            for(int i=0;i<s.length();i++){
                int res = Math.max(lengths(s,i,i+1,0)*2,(lengths(s,i,i,0)-1)*2+1);
                ans = Math.max(ans,res);
            }
            System.out.println(ans);
        }
        sc.close();
    }
    private static int lengths(String s,int start,int end,int count){
        while(start>=0&&end<s.length()&&s.charAt(start)==s.charAt(end)){
            count++;
            start--;
            end++;
        }
        return count;
    }
}


发表于 2021-08-03 17:21:58 回复(0)
def longestPalindrome(s):
    if s == s[::-1]: return len(s)
    maxLen = 0
    for i in range(len(s)):
        #如果结果为奇数
        if i - maxLen >= 1 and s[i - maxLen - 1:i + 1] == s[i - maxLen - 1:i + 1][::-1]:
            maxLen += 2
        (3534)#如果结果为偶数
        elif i - maxLen >= 0 and s[i - maxLen:i + 1] == s[i - maxLen:i + 1][::-1]:
            maxLen += 1
    return maxLen
while True:
    try:
        a = input()
        if a:
            print(longestPalindrome(a))

    except:
        break
抄的,自己始终想不透这两行代码,希望有人能给解释下,谢谢

        if i - maxLen >= 1 and s[i - maxLen - 1:i + 1] == s[i - maxLen - 1:i + 1][::-1]:
            maxLen += 2
        elif i - maxLen >= 0 and s[i - maxLen:i + 1] == s[i - maxLen:i + 1][::-1]:
            maxLen += 1

发表于 2020-03-24 16:01:09 回复(1)

#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
    string str;
    while (getline(cin, str)) {
        int max = 1;
        for (int i = 0; i < str.size(); i++) {
            int count = 0;
            for (int left = i - 1, right = i + 1; left >= 0 && right < str.size(); left--, right++) {
                if (str[left] == str[right]) {
                    count++;
                    if (max < (2 * count + 1)) max = 2 * count + 1;
                }
                else break;
            }
        }
        for (int i = 0; i < str.size() - 1; i++) {
            int count = 0;
            for (int left = i, right = i + 1; left >= 0 && right < str.size(); left--, right++) {
                if (str[left] == str[right]) {
                    count++;
                    if (max < (2 * count)) max = 2 * count;
                }
                else break;
            }
        }
        cout << max << endl;
    }
    return 0;
}
发表于 2019-03-24 15:21:38 回复(1)
LeetCode上原题:用动态规划
#include <iostream>
#include <vector>
#include <string>
#include <cctype>
#include <string.h>

using namespace std;

int main()
{
    string str;
    while(cin>>str)
    {
        int len = str.size();
        vector< vector< bool>> f( len, vector< bool >( len ) );
        int maxlen = 1;//返回子串的起点和长度

        for( int i = 0 ; i < len ; i++ )
        {
            f[ i ][ i ] = 1;
            for( int j = 0 ; j < i ; j++ )//[j,i]
            {
                f[ j ][ i ] = ( str[ j ] == str[ i ] &&
                                ( i == j + 1 || f[ j + 1 ][ i - 1 ] ) );//注意此处的理解
                if( f[ j ][ i ] && i - j + 1 > maxlen )
                {
                    maxlen = i - j + 1;
                }
            }
        }
        
        cout<<maxlen<<endl;
        
    }
    return 0;
}

发表于 2017-09-04 16:09:38 回复(0)
// 最长回文子串
import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        while(sc.hasNext()){
            String st=sc.next();
            int maxLen=0;
            for(int i=1;i<st.length()-1;i++){
                int j=0;
                for(j=1;i-j>=0 && i+j<st.length();j++){
                    if(st.charAt(i-j)!=st.charAt(i+j))
                        break;
                }
                maxLen=Math.max(maxLen,2*j-1);
                if(st.charAt(i)==st.charAt(i+1)){
                    for(j=1;i-j>=0 && i+1+j<st.length();j++){
                        if(st.charAt(i-j)!=st.charAt(i+1+j))
                            break;
                    }
                    maxLen=Math.max(maxLen,2*j);
                }
            }
            System.out.println(maxLen);
        }
    }
}

发表于 2017-08-22 10:52:43 回复(1)
//同楼上一位答主一样,以每个字符(奇数长度的回文串)或者字符间空隙
//(偶数长度的回文串)分别向左向右扩充,记录遇到的最大长度
import java.util.Scanner;


public class Main{
	public static int process(String str){
		int len=str.length();
		if(len<1){
			return 0;
		}
		int max=1;//只要字符创长度大于1,则最短的回文串长度为1
		//考虑奇数个数的回文串
		for(int i=1;i<len-1;i++){
			int k=i-1,j=i+1;
			int count=0;
			while(k>=0 && j<=len-1){
				if(str.charAt(k--)==str.charAt(j++)){
					count++;
				}else{
					break;
				}
			}
			max= (max>=(count*2+1)) ? max:(count*2+1);
		}
		//现在考虑偶数回文串的情况,主要考虑字符之间的位置
		for(int i=1;i<len-1;i++){
			int k=i-1,j=i;
			int count=0;
			while(k>=0 && j<=len-1){
				if(str.charAt(k--)==str.charAt(j++)){
					count++;
				}else{
					break;
				}
			}
			max= (max>=count*2) ? max : (count*2);
		}
		return max;
	}
	public static void main(String[] args){
		Scanner in=new Scanner(System.in);
		while(in.hasNext()){
			String str=in.next();
			System.out.println(process(str));
		}
		in.close();
	}
}






编辑于 2016-09-02 12:25:09 回复(1)
我这个代码只能过17个用例,到长度1712的用例就超时了,大佬们能帮忙看看还能怎么优化吗?
s = input()
n = len(s)
dp = [[0 for _ in range(n)]for _ in range(n)]
dp[n-1][n-1]=1
for i in range(n-1):
    dp[i][i]=1
    dp[i][i+1] = 2 if s[i]==s[i+1] else 1
for L in range(n-3,-1,-1):
    for R in range(L+2,n):
        temp = s[L:R+1]
        dp[L][R] = max(dp[L+1][R],dp[L][R-1])
        if temp==temp[::-1]:
            dp[L][R]=max(dp[L][R],len(temp))
print(dp[0][n-1])

发表于 2022-08-18 15:20:45 回复(0)
去他的动态规划,直接暴力解
import java.util.Scanner;

public class Main{
    public static int lenRoundStr(String s, int left, int right){
        while(left>=0 && right<s.length() && s.charAt(left) == s.charAt(right)){
            left--;
            right++;
        }
        return right - left - 1;
    }
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        String pw = sc.nextLine();
        int maxValue = Integer.MIN_VALUE;
        for(int i=0; i<pw.length()-1; i++){
            int ans = Math.max(lenRoundStr(pw, i, i), lenRoundStr(pw, i, i+1));
            maxValue = Math.max(ans, maxValue);
        }
        maxValue = Math.max(lenRoundStr(pw, pw.length()-1, pw.length()-1), maxValue);
        System.out.println(maxValue);
    }
}

发表于 2022-04-24 17:31:05 回复(0)
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main(){
    string s;
    while(cin>>s){
        int n=s.size();
        //求最长回文子串
        vector<vector<bool>> dp(n,vector<bool>(n,false));
        for(int i=0;i<n;i++){
            dp[i][i]=true;
        }
        int ans=0;
        for(int i=n-2;i>=0;i--){
            for(int j=i+1;j<n;j++){
                if(s[i]==s[j]){
                    if(j-i>2){
                        if(dp[i+1][j-1]){
                            dp[i][j]=true;
                            ans=max(ans,j-i+1);
                        }
                    }else{
                        dp[i][j]=true;
                        ans=max(ans,j-i+1);
                    }
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

发表于 2022-04-09 15:02:33 回复(0)
双指针,从中间往两边遍历。
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int main()
{
    string str;
    cin >> str;
    int leng = str.length();
    int sum = 0;
    for (int i = 1; i < leng - 1; i++)
    {
        int n = 0;
        int m = 0;
        int a = i;
        int b = i;
        int c = i;
        int d = i;
        while (1)
        {
            if (a == 0 || b == leng || c == -1 || d == leng)
                break;
            if (str[a - 1] == str[b + 1])
            {
                n++;
                a--;
                b++;
            }
            else if (str[c] == str[d + 1])
            {
                m++;
                c--;
                d++;
            }
            else {
                break;
            }
        }
        if (m == 0)
        {
            sum = max(sum, 2 * n + 1);
        }
        else {
            sum = max(sum, 2 * m );
        }
    }
    cout << sum;
}
发表于 2021-10-28 01:16:29 回复(0)
//中心扩散算法
import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            String s = sc.nextLine();
            int ans = maxLen(s);
            System.out.println(ans);
        }
    }
    
    private static int maxLen(String s){
        if(s==null || s.length()==0) return 0;
        if(s.length()==1) return 1;
        
        int max = 0;
        int len = s.length();
        for(int i=0; i<s.length(); i++){
            int cur = 1;
            int left=i-1, right=i+1;
            while(left>=0 && s.charAt(left)==s.charAt(i)){//左边相同移动
                left--;
                cur++;
            }
            while(right<len && s.charAt(right)==s.charAt(i)){//右边相同移动
                right++;
                cur++;
            }
            while(left>=0 && right<len && s.charAt(left)==s.charAt(right)){//两边同时相同移动
                left--;
                right++;
                cur += 2;
            }
            if(cur>max) max = cur;
        }
        return max;
    }
}

发表于 2021-08-04 18:35:51 回复(0)