博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
算法笔记_055:蓝桥杯练习 Tricky and Clever Password (Java)
阅读量:5097 次
发布时间:2019-06-13

本文共 7066 字,大约阅读时间需要 23 分钟。

目录

 


1 问题描述

问题描述
  在年轻的时候,我们故事中的英雄——国王 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 。

 

 


2 解决方案

上述问题,我的解法在蓝桥杯中最终运行评分为40分,原因:运行超时。应该是解法思路没有排除某些情况,导致最终运行超时。这篇文章仅仅用于记录自己解决此问题的思考过程,不算作标答,如果能够对于其他同学解答此题带来一些启发,那也算体现出本文的价值了吧。

 

首先,说一下我解答本题的思路:

输入一段字符串组成为: A + prefix + B + middle + C + suffix 。现在要求取 prefix + middle + suffix ,由于题目说:他选定一个整数 X ,保证 X 不小于 0 ,且 2X 严格小于串长度。那么X可取0。即当X = 0时,也只有一段——middle。

现在要求取最长的密码串:

(1)首先求取原字符串中的最长回文串长度,用maxLen0表示。

(2)由于suffix在字符串尾部,先将suffix反转,然后使用KMP算法,在原字符串中找到第一次与suffix匹配的字符串,记录suffix长度文len1。然后,求取者两端匹配字符串中间剩余的一段字符串的最大回文串的长度len2。此时,求取的密码长度maxLen = 2*len1 + len2。

在这种情况下,影响maxLen长度的因素就是初始选择的len1长度,len1长度变化,也会导致len2长度的变化。所以,我这里采用1 <= len1 < 原始字符串一半的方法来枚举,求取其中的最大maxLen。个人感觉就是在这里,才导致运算超时...

 

不多说了,下面贴代码

具体代码如下:

package com.liuzhen.systemExe;import java.util.ArrayList;import java.util.Scanner;public class Main{    //找出字符串arrayA[i]到arrayA[j]中的最大回文串,并返回其初始位置和长度(PS:此方法称作中心扩展法)    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;    }    //此处代码是使用Manacher算法求取最大回文串,但是在蓝桥杯练习系统中评分时,也是运行超时,所以在求最大回文串就采用上面更易理解的方法//    public int[] getManacher(char[] arrayA, int i, int j) {//        int[] result = new int[2];//        ArrayList
listA = new ArrayList
();// for(int y = i;y <= j;y++) {// listA.add('#');// listA.add(arrayA[y]);// }// listA.add('#');// int[] P = new int[listA.size() + 1];// int mx = 0;// int d = 0;// for(int y = 1;y <= listA.size();y++) {// if(mx > y) {// P[y] = (P[2 * d - y] > (mx - y) ? (mx - y) : P[2 * d - y]);// } else {// P[y] = 1;// }// // while(y + P[y] < listA.size() && y - P[y] >= 0 && listA.get(y + P[y]) == listA.get(y - P[y])) // P[y]++;// // if(mx < y + P[y]) {// mx = y + P[y];// d = y;// } // }// // int max = 0;// int tempY = 0;// for(int y = 0;y < P.length;y++) {// if(P[y] > max) {// max = P[y];// tempY = y;// }// }// int begin = (tempY - 1)/2;// begin = begin - (max - 1) / 2 + i + 1;// result[0] = begin;// result[1] = max - 1; // 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); }}

运行结果:

请输入一个字符串:asdfsfaaaaa17 5

 

 

 

 

转载于:https://www.cnblogs.com/liuzhen1995/p/6479907.html

你可能感兴趣的文章
【原】Python基础-序列
查看>>
【平差软件学习---科傻】 一、认识和安装科傻
查看>>
(1009) HDU 6446 Tree and Permutation(规律+树上各个点的距离和)
查看>>
应用层协议
查看>>
android 解决ScrollView嵌套ListView的问题,不能全屏,全屏不能显示下面控件
查看>>
Linux多线程实例 定时重启httpd和mysqld
查看>>
2019 Multi-University Training Contest 2 - Keen On Everything But Triangle
查看>>
centos7 中samba 开机启动命令
查看>>
对“点餐系统”的阅读、分析与运行
查看>>
Linux 安装Mysql
查看>>
给定两个有序的整形数组,找出里边的相同元素
查看>>
C语言指针加1问题以及字节对齐问题
查看>>
【NLP】揭秘马尔可夫模型神秘面纱系列文章(二)
查看>>
rest-work-eat-study-rest-work-eat or rest-rest-work-work-eat-eat..
查看>>
用EnableMenuItem不能使菜单变灰的原因
查看>>
Mac OS X Yosemite安装Hadoop 2.6记录
查看>>
Tomcat全攻略
查看>>
闰年的定义
查看>>
探索Scala(1)-- 运算符重载
查看>>
【LDAP】LDAP 中 CN, OU, DC 的含义
查看>>