时间:2021/02/25
一.题目描述
玛雅人有一种密码,如果字符串中出现连续的2012四个数字就能解开密码。给一个长度为N的字符串,(2=<N<=13)该字符串中只含有0,1,2三种数字,问这个字符串要移位几次才能解开密码,每次只能移动相邻的两个数字。例如02120经过一次移位,可以得到20120,01220,02210,02102,其中20120符合要求,因此输出为1.如果无论移位多少次都解不开密码,输出-1。
输入描述
输入包含多组测试数据,每组测试数据由两行组成。 第一行为一个整数N,代表字符串的长度(2<=N<=13)。 第二行为一个仅由0、1、2组成的,长度为N的字符串。
输出描述
对于每组测试数据,若可以解出密码,输出最少的移位次数;否则输出-1。
题目链接
https://www.nowcoder.com/practice/761fc1e2f03742c2aa929c19ba96dbb0?
tpId=40&tqId=21343&rp=1&ru=%2Fta%2Fkaoyan&qru=%2Fta%2Fkaoyan%2Fquestion-ranking&tab=answerKey
二.算法
题解
这道题用到了图的宽度优先搜索,需要用到队列这种数据结构。主要解决两个问题:如何存储一个字符串交换的次数以及如何知道这个字符串之前已经存储过。对于第一个问题,我们创建了一个内部静态数组,通过它的step属性来存放交换次数。对于第二个问题,我们利用Set的特性来解决,Set中不允许有重复元素。对于队列,我们使用Java中的LinkedList模拟,首先弹出队首元素,然后判读该元素中是否含有2012,若没有,则对该元素从头到尾进行一次交换,对于一次交换产生的之前没有的元素存入队列中,存入时交换次数加一,这里要注意交换后要交换回来,因为每次交换都是在原字符串的基础上进行的。个人教训:虽然String类型是引用数据类型,但是swap函数中是将String转换为char数组的形式再交换,所以没有用到String引用数据类型的特性,最后还有返回交换后的字符串。
重点
图的宽度优先搜索(BFS)
代码
import java.util.Scanner; import java.util.HashSet; import java.util.LinkedList; public class Main{ public static void main(String[] args){ Scanner in = new Scanner(System.in); while(in.hasNext()){ int n = in.nextInt(); String str = in.next(); int count = bfs(str); System.out.println(count); } } public static class Node{ int step; String s; public Node(String s, int step){ this.s = s; this.step = step; } } public static int bfs(String str){ HashSet<String> set = new HashSet<>(); LinkedList<Node> list = new LinkedList<>(); Node node = new Node(str, 0); set.add(str); list.add(node); while(list.size() != 0){ Node n = list.poll(); String s = n.s; if(s.contains("2012")){ return n.step; } else{ for(int i = 0; i < str.length() - 1; i++){ s = swap(s, i); if(set.add(s) == true){ Node a = new Node(s, n.step + 1); list.add(a); } s = swap(s, i); } } } return -1; } public static String swap(String s, int i){ char[] ch = s.toCharArray(); char temp = ch[i]; ch[i] = ch[i + 1]; ch[i + 1] = temp; return String.valueOf(ch); } }