蓝桥杯-Tricky and Clever Password(java)

					算法训练 Tricky and Clever Password  
					时间限制:2.0s   内存限制:256.0MB
			   
			问题描述
			  在年轻的时候,我们故事中的英雄——国王 Copa——他的私人数据并不是完全安全地隐蔽。对他来说是,这不可接受的。因此,他发明了一种密码,好记又难以破解。后来,他才知道这种密码是一个长度为奇数的回文串。
			
			  Copa 害怕忘记密码,所以他决定把密码写在一张纸上。他发现这样保存密码不安全,于是他决定按下述方法加密密码:他选定一个整数 X ,保证 X 不小于 0 ,且 2X 严格小于串长度。然后他把密码分成 3 段,最前面的 X 个字符为一段,最后面的 X 个字符为一段,剩余的字符为一段。不妨把这三段依次称之为 prefix, suffix, middle 。显然, middle 的长度为一个大于 0 的奇数,且 prefix 、 suffix 的长度相等。他加密后的密码即为 A + prefix + B + middle + C + suffix ,其中 A 、 B 、 C 是三个由 Copa 选定的字符串,且都有可能为空, + 表示字符串相连。
			
			  许多年过去了。Copa 昨天找到了当年写下加密后字符串的那张纸。但是,Copa 把原密码、A、B、C 都忘了。现在,他请你找一个尽量长的密码,使得这个密码有可能被当年的 Copa 发明、加密并写下。
			输入格式
			  输入包含一个只含有小写拉丁字母的字符串,长度在 1 到 10^5 之内。
			输出格式
			  第一行包含一个整数 k ,表示你找到的原密码分成的 3 个部分中有多少个非空字符串。显然 k in {1, 3} 。接下来 k 行,每行 2 个用空格分开的整数 x_i l_i ,表示这一部分的起始位置和长度。要求输出的 x_i 递增。
			
			  起始位置 x_i 应该在 1 到加密后的字符串长度之间。 l_i 必须是正整数,因为你只要输出非空部分的信息。 middle 的长度必须为奇数。
			
			  如果有多组答案,任意一组即可。提示:你要最大化的是输出的 l_i 的总和,而不是 k 。
			样例输入
			abacaba
			样例输出
			1
			1 7
			样例输入
			axbya
			样例输出
			3
			1 1
			2 1
			5 1
			样例输入
			xabyczba
			样例输出
			3
			2 2
			4 1
			7 2
			数据规模和约定
			  对于 10% 的数据: n <= 10
			
			  对于 30% 的数据: n <= 100
			
			  对于 100% 的数据: n <= 100000
			
			  存在 20% 的数据,输出文件第一行为 1 。


package com.sihai.advance;

import java.util.ArrayList;
import java.util.Scanner;

public class Main{
    
    public int[] getMaxReverse(char[] arrayA, int i, int j) {
        int[] result = new int[2];
        int max = 1;
        int begin = i+1;    //最大回文串的开始位置,初始化为字符i的位置
        int zero = i;      //保存i的初始值
        for(i = i + 1;i < j;i++) {
            int start = i - 1;
            int end = i + 1;
            int count = 1;
            while(start >= zero && end <= j) {    //对于此种情况,只返回奇数位回文串
                if(arrayA[start] == arrayA[end]) {
                    start--;
                    end++;
                    count = count + 2;
                }
                else
                    break;
            }
            if(count > max) {
                max = count;
                begin = i - (count-1)/2 + 1;
            }
        }
        result[0] = begin;   //最大回文串开始位置
        result[1] = max;     //最大回文串长度
    
        return result;
    }
   
    //使用KMP模式匹配,计算next函数值
    public int[] getNext(char[] arrayB) {
        int[] next = new int[arrayB.length + 1];
        int j = 0;
        for(int i = 1;i < arrayB.length;i++) {
            while(j > 0 && arrayB[i] != arrayB[j]) j = next[j];
            if(arrayB[i] == arrayB[j]) j++;
            next[i+1] = j;
        }
        return next;
    }
    //使用KMP算法,找出字符数组arrayB,在arrayA中第一次出现匹配的位置
    public int getKmpFirstPosition(char[] arrayA, char[] arrayB) {
        int position = -1;
        int j = 0;
        int[] next = getNext(arrayB);
        for(int i = 0;i < arrayA.length;i++) {
            while(j > 0 && arrayA[i] != arrayB[j]) j = next[j];
            if(arrayA[i] == arrayB[j])
                j++;
            if(j == arrayB.length) {
                position = i - j + 1;
                break;
            }
        }
        return position;
    }
    
    public void printResult(String A) {
        //当选定整数X = 0时,输出第一行为1,此时只需在A中直接找到有段最长回文串即可,这时的密码最长
        char[] arrayA = A.toCharArray();
        int[] result0 = getMaxReverse(arrayA, 0, arrayA.length-1);
        int maxLen0 = result0[1];   //获得此时回文串的最大长度
        //当X != 0时,最长密码其尾部一定在输入的字符串尾部,即 suffix不为空
        int j = arrayA.length - 1;
        int maxLen = 0;     //用于计算最长密码,初始化为0
        int position1 = 0;
        int position2 = 0;
        int position3 = 0;
        int len1 = 0;
        int len2 = 0;
         for(int lenS = 1;lenS < arrayA.length/2;lenS++) {
            char[] tempS = new char[lenS];
            for(int a = 0;a < lenS;a++) 
                tempS[a] = arrayA[j-a];
            if(getKmpFirstPosition(arrayA, tempS) == -1) {
                continue;
            } 
            int tempPosition1 = getKmpFirstPosition(arrayA, tempS) + 1;
            int endPosition1 = tempPosition1+lenS;
            int startPosition3 = arrayA.length-lenS;
            
            if(startPosition3 <= endPosition1)
                continue;
    
            int[] result = getMaxReverse(arrayA,tempPosition1+lenS-1,j-lenS);
            int tempLen2 = result[1];
            int tempPosition2 = result[0];
            
            if(lenS * 2 + tempLen2 > maxLen) {
                position1 = tempPosition1;
                position2 = tempPosition2;
                position3 = j - lenS + 2;
                len1 = lenS;
                len2 = tempLen2;
                maxLen = lenS * 2 + tempLen2;
            }
        }
         if(maxLen0 >= maxLen) {  
             System.out.println("1");
              System.out.println(result0[0]+" "+result0[1]);
         } else {
             System.out.println("3");
             System.out.println(position1+" "+len1);
             System.out.println(position2+" "+len2);
             System.out.println(position3+" "+len1);
         }
    }
    
    public static void main(String[] args){
        Main test = new Main(); 
        Scanner in = new Scanner(System.in);
    //    System.out.println("请输入一个字符串:");
        String A = in.nextLine();
        test.printResult(A);
    }
}



运行结果:

请输入一个字符串:
asdfsfaaaaa
1
7 5

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hello-java-maker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值