题目:
输入长度为N,且只含'0''1''2'的字符串(2=<N<=13),问每次移动相邻的两个数字,最少经过多少次,可使字符串含有"2012"。如果无解,输出-1。(例如02120经过一次移位,可以得到20120,01220,02210,02102,其中20120符合要求,因此输出为1。)
思路:
广度优先遍历(BFS),过程借用队列。
字符串通过1次移动产生的所有新子串,为其子结点。去除和爷爷结点以及父亲结点相同的子结点,然后其余入队。再出队,遍历。
如此循环,直到产生解。
学习体会:
1.程序中的isHave2012()函数,其实是模式匹配函数,其有改进算法,参见《数据结构》。
2.相关的模式匹配函数有:
字符匹配:在<string.h>中有char* strchr(char *s,char c):字符c在字符串s中首次出现的地址。
字符串匹配:在<string>中有int find(char *s),或int find(string s):找到,返回s在第一次出现的字符串数组下标,否则返回string::npos(这是一个很大的数)。
3.&&能连用:(i==1 && j==2 && k==3)。
#include <iostream>
#include <stdio.h>
#include <queue>
using namespace std;
//字符串结构体
struct MaYaString{
char maya[14]; //玛雅数字字符串
int level; //得到该字符串所交换的次数
int exc; //父串下标为exc和(exc+1)的字符交换得到该串
};
//判断两字符串是否相同
bool isSame(char *a,char *b,int n);
//判断一个字符串是否含2012
bool isHave2012(char *a,int n);
//判断一个字符串是否可能移位成含有2012
bool isAble(char *a,int n);
int main(){
int N,i,j,answer; //N:字符串长度,answer:交换次数
char cc;
queue<MaYaString *> Que; //队列,方便广度遍历
bool has2012; //表示2012是否已经出现
while(scanf("%d",&N)!=EOF){ //输入字符串长度
answer=0; //初始化一些变量
has2012=false;
MaYaString *Node1=new MaYaString;
MaYaString *Node2;
Node1->level=0;
Node1->exc=-1;
scanf("%s",Node1->maya); //输入数字字符串
if(isHave2012(Node1->maya,N)){ //输入本身已经含有2012,则直接输出
printf("0\n");
delete Node1;
}
else{ //输入本身不含2012
if(!isAble(Node1->maya,N)){ //输入不可能交换出2012
printf("-1\n");
delete Node1;
}
else{ //输入可以交换出2012
Que.push(Node1); //结点Node1入队
while(!Que.empty()){ //队不为空,则一直循环
Node1=Que.front(); //Node1出队
Que.pop();
for(i=0;i<N-1;i++){ //循环产生Node1结点的子结点
if(Node1->exc==i) //剪枝:不让生成和爷爷结点一样的串
continue;
Node2=new MaYaString; //子结点Node2初始化
Node2->exc=i;
Node2->level=(Node1->level)+1;
for(j=0;j<N;j++){
Node2->maya[j]=Node1->maya[j];
}
cc=Node2->maya[i];
Node2->maya[i]=Node2->maya[i+1];
Node2->maya[i+1]=cc;
if(isSame(Node1->maya,Node2->maya,N)){ //剪枝:不让生成和父亲结点一样的串
delete Node2;
continue;
}
if(isHave2012(Node2->maya,N)){ //子结点Node2有2012了
has2012=true; //表已产生答案,且记下答案
answer=Node2->level;
delete Node2; //释放所有空间
while(!Que.empty()){
Node2=Que.front();
Que.pop();
delete Node2;
}
break;
}
else{ //子结点Node2还没有2012,入队
Que.push(Node2);
}
}
delete Node1; //删除释放Node1
}
if(has2012){ //已有答案
printf("%d\n",answer); //输出答案:交换次数
}
}
}
}
return 0;
}
//判断两字符串是否相同
bool isSame(char *a,char *b,int n){
int i;
bool boo=true;
for(i=0;i<n;i++){
if(a[i]!=b[i]){
boo=false;
break;
}
}
return boo;
}
//判断一个字符串是否含2012
bool isHave2012(char *a,int n){
int i;
bool boo=false;
if(n<4){
return false;
}
else{
for(i=0;i<=(n-4);i++){
if((a[i]=='2' && a[i+1]=='0') && (a[i+2]=='1' && a[i+3]=='2')){
boo=true;
break;
}
}
}
return boo;
}
//判断一个字符串是否可能移位成含有2012
//只要至少有2个'2',1个'0',1个'1',就可以
bool isAble(char *a,int n){
int i,n0=0,n1=0,n2=0;
if(n<4)
return false;
else{
for(i=0;i<n;i++){
switch(a[i]){
case '0':n0++;break;
case '1':n1++;break;
case '2':n2++;
}
}
if(n0>=1 && n1>=1 && n2>=2)
return true;
else
return false;
}
}