洛谷 P1032 【字串变换】

主要思想:queue+map+set

说一下读入:while(cin>>x>>y),这样就行了,按ctrl+z结束输入

首先,我们很容易想到一种做法:

用队列进行宽搜(用结构体/二维数组)
map作为变换规则的储存器
set用来查重
每次在队首里找是否有能变换的部分,然后判重,入队

于是就有了以下的代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<map>
#include<string>
#include<set>
using namespace std;
struct node{
	string s;//当前的字符串
	int step;//所用的步数
};
queue<node> q;//队列
string a,b;//初始的两个串
map<string,string> m;//存规则
map<string,string>::iterator it;//要用迭代器遍历
set<string> vis;//判重
void bfs(){
	while(!q.empty()){//队列不空
		node z=q.front();//取出队首
		q.pop();//队首出队
		string x=z.s;//取元素
		int y=z.step;//取元素
		if(y>10){//超过10步了
			cout<<"NO ANSWER!"<<endl;
			return;
		}
		for(it=m.begin();it!=m.end();it++){//迭代器的遍历方式
			string strnew=x;//如果一直用x,那么难以保证其值没有发生改变
			string strx=it->first;//取<string,string>中的前一个
			string stry=it->second;//取后一个
			int stepnew=y;//同strnew的原因
			if(((long long)strnew.find(strx))!=-1){//如果当前字符串中有这个可改变的部分
				strnew.replace(strnew.find(strx),strx.size(),stry);//从这部分的起始位出发,把整个部分换成要变的部分
				if(vis.find(strnew)==vis.end()){//集合中原来没有这个字符串
					vis.insert(strnew);//放进去
					node newpush;//准备入队
					newpush.s=strnew;//赋值
					newpush.step=stepnew+1;//多一步了
					if(strnew==b){//已经得到答案了
						cout<<stepnew+1<<endl;//直接输出
						return;
					}
					else q.push(newpush);//入队
				}
			}
		}
	}
	cout<<"NO ANSWER!"<<endl;//循环跑完了,说明找不到答案
	return;
}
int main(){
	cin>>a>>b;
	string x,y;
	while(cin>>x>>y)
		m[x]=y;//保存规则
	node z;
	z.step=0;z.s=a;//一开始是0步
	q.push(z);//初始入队
	vis.insert(a);//放到集合里
	bfs();//进行宽搜
	return 0;
}

是不是看上去挺好的?但只有60分

下载数据,你会发现:原来一个部分可能可以有多种情况可以变化!!!

举个栗子例子:

y abc

y d

那么,由于我们采用下标方式赋值,map[y]就会与最后一个读入的值相同,即map[y]=d;

而如果用insert赋值,map[y]就会与第一个读入的值相同,即map[y]=abc

然后就······没有办法了

STL真是一项伟大的发明!

除了map,还有——multimap

它支持映射多个值,跟上面对比起来看

y abc

y d

那么y就既可以等于abc,又可以等于d

我一开始见到multimap还认为为什么有人这么傻不想要自动去重

但这时候就不支持下标赋值了,只能用insert不然不能显出其特殊之处

但在遍历时,认为map<y,abc>与map<y,d>是分开的,遍历方式是一样的

于是修改部分代码:

	multimap<string,string> m;
	multimap<string,string>::iterator it;
        ······
	string x,y;
	while(cin>>x>>y)
	m.insert(make_pair(x,y));

(注:头文件还是map)

然而······还是WA了

继续下载数据,发现:一个字符串里可能有多个相同的部分可以变换!而如果用find函数,只能找出一个

于是——两重循环暴力走起!

暴力大法好!幸好出题人十分有良心没有出大数据

再次改动部分代码:

	if(strx.size()>strnew.size())continue;//防止负数,不然莫名其妙错,我也不知道为什么(在exe里就会错)
	for(int i=0;i<=strnew.size()-strx.size();i++){
				string strnew=x;//上面其实已经赋过一遍值了,变量名也是一样的
				string strx=it->first;//此处还是为了避免值被改掉
				string stry=it->second;
				int stepnew=y;
				bool flag=true;//标记
				for(int j=0;j<strx.size();j++)
					if(strx[j]!=strnew[i+j]){//两者有不一样之处,说明不能变
						flag=false;
						break;
					}
				 if(flag==false)continue;//判断条件成立就得用大括号把下面整个部分包起来,当时懒得写了
				 ······	
}

然后这道题才算完真是毒瘤啊,为什么不评个蓝题

最后给大家完整AC代码:

#include<iostream>
#include<cstdio>
#include<queue>
#include<map>
#include<string>
#include<set>
using namespace std;
struct node{
	string s;
	int step;
};
queue<node> q;
string a,b;
multimap<string,string> m;
multimap<string,string>::iterator it;
set<string> vis;
void bfs(){
	while(!q.empty()){
		node z=q.front();
		q.pop();
		string x=z.s;
		int y=z.step;
		if(y>10){
			cout<<"NO ANSWER!"<<endl;
			return;
		}
		for(it=m.begin();it!=m.end();it++){
			string strnew=x;
			string strx=it->first;
			string stry=it->second;
			int stepnew=y;
			if(strx.size()>strnew.size())continue;
			for(int i=0;i<=strnew.size()-strx.size();i++){
				string strnew=x;
				string strx=it->first;
				string stry=it->second;
				int stepnew=y;
				bool flag=true;
				for(int j=0;j<strx.size();j++)
					if(strx[j]!=strnew[i+j]){
						flag=false;
						break;
					}
				if(flag==false)continue;
				strnew.replace(i,strx.size(),stry);
				if(vis.find(strnew)==vis.end()){
					vis.insert(strnew);
					node newpush;
					newpush.s=strnew;
					newpush.step=stepnew+1;
					if(strnew==b){
						cout<<stepnew+1<<endl;
						return;
					}
					else q.push(newpush);
				}
			}
		}
	}
	cout<<"NO ANSWER!"<<endl;
	return;
}
int main(){
	cin>>a>>b;
	string x,y;
	while(cin>>x>>y)
	m.insert(make_pair(x,y));
	node z;
	z.step=0;z.s=a;
	q.push(z);
	vis.insert(a);
	bfs();
	return 0;
}

大部分代码都与第一份差不多,故不再重写注释,望谅解主要是打了这么多字受累了

累死我了,客官不点个赞?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值