首页 > 搜索 > DFS搜索 > UVa-129-Krypton Factor[DFS]
2014
01-28

UVa-129-Krypton Factor[DFS]

Time limit: 3.000 seconds
限时:3.000秒

Problem
问题

You have been employed by the organisers of a Super Krypton Factor Contest in which contestants have very high mental and physical abilities. In one section of the contest the contestants are tested on their ability to recall a sequence of characters which has been read to them by the Quiz Master. Many of the contestants are very good at recognising patterns. Therefore, in order to add some difficulty to this test, the organisers have decided that sequences containing certain types of repeated subsequences should not be used. However, they do not wish to remove all subsequences that are repeated, since in that case no single character could be repeated. This in itself would make the problem too easy for the contestants. Instead it is decided to eliminate all sequences containing an occurrence of two adjoining identical subsequences. Sequences containing such an occurrence will be called “easy”. Other sequences will be called “hard”.
“超级氪因素大赛”(译注:英国的一档电视心智竞答节目)的主办方雇你来对付那些足智多谋的参赛选手。在比赛的一个环节中,节目主持人将念出一长串的字母来考验选手的记忆能力。因为许多选手都是分析字串模式的高手,为了增加一些比赛的难度,主办方决定不再使用那些含有特定重复子串的字串。但是他们又不能将所有重复的子串都删掉,如果那样的话字串中就不存在两个相同的单字了,这反倒会让问题变的非常简单。为了解决这一问题,他们决定仅删除那些包含相邻重复子串的字串。我们将存在上述相邻重复情况的字串称为“easy”(简单),否则称为“hard”(难)。

For example, the sequence ABACBCBAD is easy, since it contains an adjoining repetition of the subsequence CB. Other examples of easy sequences are:
比方说,字串ABACBCBAD就是一个“easy”,因为它里面有相邻的重复子串“CB”。另举一些“easy”字串的例子如下:

  • BB
  • ABCDACABCAB
  • ABCDABCD

Some examples of hard sequences are:
下面是一个“hard”字串的例子:

  • D
  • DC
  • ABDAB
  • CBABCBA

 

Input and Output
输入和输出

In order to provide the Quiz Master with a potentially unlimited source of questions you are asked to write a program that will read input lines that contain integers n and L (in that order), where n > 0 and L is in the range 1 ≤ L ≤ 26, and for each input line prints out the nth hard sequence (composed of letters drawn from the first L letters in the alphabet), in increasing alphabetical order (alphabetical ordering here corresponds to the normal ordering encountered in a dictionary), followed (on the next line) by the length of that sequence. The first sequence in this ordering is A. You may assume that for given n and L there do exist at least n hard sequences.
为了能给节目主持人提供无限量的问题字串,要求你来写一个程序执行生成运算。程序从输入中读取多行数据,每行包括两个整数n和L(即按此顺序给出),其中n > 0,L的范围是1 ≤ L ≤ 26。根据这些输入,程序要按照字母表升序打印出第n个“hard”字串(由字母表中的前L个字母构成),并在接下来的一行打印这个串的长度。按照上述规则,第一个串应该是“A”。对于给定的n和L,你可以认为第n个“hard”串是一定存在的。

For example, with L = 3, the first 7 hard sequences are:
比方说,当L = 3时,头7个“hard”字串为:

A
AB
ABA
ABAC
ABACA
ABACAB
ABACABA

As each sequence is potentially very long, split it into groups of four (4) characters separated by a space. If there are more than 16 such groups, please start a new line for the 17th group.
字串可能很长,因此要将它们分成4个字为一组,中间用空格隔开。如果超过16组,则换一行,再接着输出第17组。

Therefore, if the integers 7 and 3 appear on an input line, the output lines produced should be:

按照此格式,对于输入的整数7和3,输出应该为:

ABAC ABA
7

Input is terminated by a line containing two zeroes. Your program may assume a maximum sequence length of 80.
输入由一行两个零表示结束。你的程序可以限定最大的字串长度为80。

 

Sample Input
输入示例

30 3
0 0

Sample Output
输出示例

ABAC ABCA CBAB CABA CABC ACBA CABA
28

 

Analysis
分析

很经典的递归调用过程,难度适中,推荐先认真的做一下再来看解析。题目本身不是很好懂,需要认真思考才能明白其意图。

避免相邻重复的子串很好办:如果生成字符串是逐个在后面添加字符的话,只要每次在添加后检查当前串是否满足条件就可以了。不满足的不能添加,这样就能保证字符串里没有任何相邻重复的子串。检查的方法是从长度1开始,检查最后1个字符及其前面1个字符是否相等,再将长度增为2,检查最后2个字符及其前面2个字符是否相等,以此类推。

题目要求按字母表顺序生成字串,通过观查给出的例子(7 3)可知字串都是以A开始,逐个向后生成。每次在末尾添加一个尽量靠前的(在ASCII表中较小的)又可以满足要求的字母,如果都不满足要求则向前回溯,将前一个字母增大再试。每次成功的添加一个都算作生成了一个字串。直到生成的字串数达到给定的数量n。

思路理清后代码就很容易写了,算是一个小程序。有一点要注意,输出时一定要按照要求的格式,每过四个字符加一个空格,达到4×16的字符后不要加空格,而要加一个回车。后面的仍是每过四个加一个空格。千万注意!

 

Solution
解答

#include <iostream>
#include <string>
using namespace std;
//递归过程,顺序生成所有的字串。str保存结果,n和L为题目中给出的同名变量
void Sequence(string &str, int &n, int L) {
	//记录字串长度,以加快运算
	int nLen = str.length(), nHalf = (str.length() + 1) / 2;
	//在结果字串后面依次尝试添加前L个大写字母
	for (char i = 'A', iEnd = L + 'A', m = 1; i < iEnd; ++i) {
		str.push_back(i); //插入当前字符
		//下面判断新生成的字符串中是否存在相邻的重复
		//第1次判断最后1个字符,第2次判断最后2个字符,以此类推
		for (m = 1; m <= nHalf; ++m) {
			//将最后的i个字符与之前的i个字符比较,如果有相同则跳出
			if (equal(str.end() - m, str.end(), str.end() - m * 2)) {
				m = 0; //将m置为0表示存在重复的相邻子串
				break; //跳出循环
			}
		}
		if (m != 0) { //如果不存在重复
			//如果生成的字串已经够数,返回上一级
			if (--n == 0) return;
			Sequence(str, n, L); //进入下一级调用
			//如果生成的字串已经够数,返回上一级
			if (n == 0) return;
		} //删除刚添加在后面的字符,保持结果字串在进入这一级时的原状
		str.erase(nLen); //准备为添加下一个字符作准备
	}
}
//主函数
int main(void) {
	for (int n, L; cin >> n >> L && n != 0; ) { //循环读取每一组输入的数据
		string str; //结果字符串
		Sequence(str, n, L); //递归生成所有的无相邻重复字串
		int nLen = str.length(); //保留生成字串的字符数量
		for (size_t i = 4; i < str.length(); i += 5) { //按格式处理字串
			//每隔4个插入一个空格,每隔80字符插入一个回车
			str.insert(str.begin() + i, i == 79 ? '\n' : ' ');
		} //输出结果字串和字符数量
		cout << str << '\n' << nLen << endl;
	}
	return 0;
}

,
  1. 题目需要求解的是最小值,而且没有考虑可能存在环,比如
    0 0 0 0 0
    1 1 1 1 0
    1 0 0 0 0
    1 0 1 0 1
    1 0 0 0 0
    会陷入死循环

  2. 第二种想法,我想来好久,为啥需要一个newhead,发现是把最后一个节点一直返回到嘴上面这层函数。厉害,这道题之前没样子想过。