NOIP2002-字串变换

Description

已知有两个字串A,BA,B及一组字串变换的规则(至多66个规则):

A_1A1​ ->B_1B1​

A_2A2​ -> B_2B2​

规则的含义为:在 AA中的子串 A_1A1​ 可以变换为B_1B1​,A_2A2​ 可以变换为 B_2B2​ …。

例如:AA='abcdabcd'BB='xyzxyz'

变换规则为:

‘abcabc’->‘xuxu’‘udud’->‘yy’‘yy’->‘yzyz’

则此时,AA可以经过一系列的变换变为BB,其变换的过程为:

‘abcdabcd’->‘xudxud’->‘xyxy’->‘xyzxyz’

共进行了33次变换,使得AA变换为BB。

Input

格式如下:

AA BB
A_1A1​ B_1B1​
A_2A2​ B_2B2​ |-> 变换规则

... ... /

所有字符串长度的上限为2020。

Output

输出至屏幕。格式如下:

若在1010步(包含1010步)以内能将AA变换为BB,则输出最少的变换步数;否则输出"NO ANSWER!"

Sample Input

abcd xyz
abc xu
ud y
y yz

Sample Output

3

Solution

题目大意是,给定起始状态A和终止转态B,以及一些变换规则,问多少步可以从A变换到B(或大于10步无解)。

显然可以用BFS搜索解决,但是注意一定要判重。这道题如果熟悉STL的话是很容易写出简洁的代码的,我用的string的replace构造转换后的字串,set判重。

这是我的第一份代码:

#include<cstdio>
#include<queue>
#include<set>
#include<utility>
#include<vector>
#include<cstring>
#include<iostream>
using namespace std;
struct node{
string s;int step;
node(string s,int step):s(s),step(step){}//构造函数
};
string A,B,x,y;
vector<pair<string,string> > rule;
set<string> used;
queue<node> q;
bool bfs(){
q.push(node(A,));
used.insert(A);
while(!q.empty())
{
node h=q.front();q.pop();
if(h.step>) return false;
for(int i=;i<rule.size();++i)
{
int x=h.s.find(rule[i].first);//x是可以转换的起始位置
if(x!=-)
for(int j=x;j!=-;j=h.s.find(rule[i].first,j+))//寻找下一个可以转换的位置
{
string tmp=h.s;
tmp.replace(j,rule[i].first.length(),rule[i].second);//把tmp从j开始的 rule[i].first.length()个字符替换成rule[i].second
if(tmp==B){cout<<h.step+;return true;}
if(!used.count(tmp)){//一定要判重
q.push(node(tmp,h.step+));
used.insert(tmp);
}
}
}
}
return false;
}
int main()
{
cin>>A>>B;
while(cin>>x>>y) rule.push_back(make_pair(x,y));
if(!bfs()) cout<<"NO ANSWER!";
return ;
}

在洛谷上28ms就过了,但交到牛客网上T得飞起,第n次被牛客网的超强数据卡了,真想剁了牛客网。

但是本蒟蒻是不会放弃的,下面重点来了:

Optimization(优化)

注意到起始状态A和终止状态B都是确定的,那么我们可不可以从正反两个方向一起向中间搜索呢?答案是肯定的,这就是双向BFS。

效率

从题目来看,每次扩展有k(k<=66)个分支,最多扩展n(n<=10)层,那么BFS的计算量最坏情况近似地为k^n,这个数字还是相当吓人的(怪不得我会T)

但如果用双向BFS的话,时间效率的优化就不仅仅是一半那么简单,而是2*(k^(n/2)),效率大大提升。

实现

实现很简单,建两个队列,一个存正向的,另一个存反向的,很容易想到正反交替扩展,但这样其实是不科学的,会导致两边决策树发展情况失衡,降低时间效率。因此最好的方式应该是选择结点数较少的一边扩展,这样可以最大限度地维持两边决策树的平衡(这里只简单说一下,具体见网上的证明)。

还有一点要注意,题目中的规则是有单向性的,所以正向扩展时应从A_1->B_1,而反向扩展时应从B_1->A_1,这害我查错查了好久。。。

双向BFS优化代码:

#include<cstdio>
#include<queue>
#include<set>
#include<map>
#include<utility>
#include<vector>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
struct node{
string s;int step;
node(string s,int step):s(s),step(step){}
};
string A,B,x,y;
vector<pair<string,string> > rule;
set<string> used[];
map<string,int> dis[];//这里要多建一个dis来存到这个状态花的步数
queue<node> q[];//每一个都建两个,下标0存正向的,下标1存反向的
void expand(int k){
node h=q[k].front();q[k].pop();
for(int i=;i<rule.size();++i)
{
int x=(k&)?h.s.find(rule[i].second):h.s.find(rule[i].first);//正反是不同的
if(x!=-)
for(int j=x;j!=-;j=(k&)?h.s.find(rule[i].second,j+):h.s.find(rule[i].first,j+))
{
string tmp=h.s;
if(k&) tmp.replace(j,rule[i].second.length(),rule[i].first);
else tmp.replace(j,rule[i].first.length(),rule[i].second);
if(used[k^].count(tmp)){cout<<h.step++dis[k^][tmp];exit();}//如果另一个方向已经访问过tmp了,两边到tmp的步数之和即解
if(!used[k].count(tmp)){
q[k].push(node(tmp,h.step+));
dis[k][tmp]=h.step+;
used[k].insert(tmp);
}
}
}
return ;
}
bool bfs(){
q[].push(node(A,)),q[].push(node(B,));
dis[][A]=,dis[][B]=;
used[].insert(A),used[].insert(B);
while(!q[].empty()&&!q[].empty())
{
if(q[].front().step+q[].front().step>) return false;
q[].size()<q[].size()?expand():expand();//扩展结点数较少的一边
}
return false;
}
int main()
{
cin>>A>>B;
while(cin>>x>>y) rule.push_back(make_pair(x,y));
if(!bfs()) cout<<"NO ANSWER!";
return ;
}

尽管我懒到用STL的queue,还是成功AC牛客网(话说我的第一份代码是不是因为这个才挂的。。。)

2018-10-20

NOIP2002-字串变换【双向BFS】的更多相关文章

  1. NOIP2002 字串变换题解(双向搜索)

    65. [NOIP2002] 字串变换 时间限制:1 s   内存限制:128 MB [问题描述] 已知有两个字串A$, B$及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ ...

  2. [NOIP2002]字串变换 T2 双向BFS

    题目描述 已知有两个字串  A,B  及一组字串变换的规则(至多6个规则): A1−>B1 A2−>B2 规则的含义为:在  A$中的子串  A1可以变换为可以变换为B1.A2可以变换为可 ...

  3. 双向BFS—>NOIP2002 字串变换

    如果目标也已知的话,用双向BFS能很大提高速度 单向时,是 b^len的扩展. 双向的话,2*b^(len/2)  快了很多,特别是分支因子b较大时 至于实现上,网上有些做法是用两个队列,交替节点搜索 ...

  4. NOIP2002字串变换[BFS]

    题目描述 已知有两个字串 A$, B$ 及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ -> B2$ 规则的含义为:在 A$中的子串 A1$ 可以变换为 B1$.A2 ...

  5. 「NOIP2002」「Codevs1099」 字串变换(BFS

    1099 字串变换 2002年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold   题目描述 Description 已知有两个字串 $A$, ...

  6. NOIP2002 字串变换

    题二 字串变换 (存盘名: NOIPG2) [问题描述]: 已知有两个字串 A$, B$ 及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ -> B2$ 规则的含义为: ...

  7. P1032 字串变换 字符串BFS

    题目描述 已知有两个字串A,BA,B及一组字串变换的规则(至多66个规则): A_1A1​ ->B_1B1​ A_2A2​ -> B_2B2​ 规则的含义为:在 AA中的子串 A_1A1​ ...

  8. codevs1099字串变换(Bfs)

    /* 最少步数问题 妥妥的Bfs 很显然队列里存的是串(可能存个数也可以 就像8数码那样) 然后每次队首元素弄出来 能换的都换一遍 最后每次换完的新串入队前先判断到头了没 最后说一句 String大法 ...

  9. 洛谷 P1032 字串变换 (BFS)

    题目传送门 我即使是死了,钉在棺材里了,也要在墓里,用这腐朽的声带喊出 STL大法好 这题最麻烦的其实是处理字符串,真正的搜索部分我个人认为也就只有橙题或黄题的难度.而处理字符串,正如前面所说,STL ...

  10. 洛谷P1032 字串变换【bfs】

    题目链接:https://www.luogu.org/problemnew/show/P1032 题意: 给定一个原字符串和目标字符串,以及几个字符串变换的规则. 问能否根据这几个规则在十步之内把原字 ...

随机推荐

  1. 设置Eclipse代码自动提示

    对于编程人员来说,要记住大量的类名或类方法的名字,着实不是一件容易的事情.如果要IDE能够自动补全代码,那将为我们编程人员带来很大帮助. Eclipse代码里面的代码提示功能默认是关闭的,只有输入“. ...

  2. [LeetCode] 223.矩形面积

    题目链接: https://leetcode-cn.com/problems/rectangle-area 难度:中等 通过率:41.3% 题目描述: 在 二维 平面上计算出两个 由直线构成的 矩形重 ...

  3. Windows7下Pycharm安装Keras

    1.安装Anaconda3 2.安装Pycharm 3.安装TensorFlow 一.File -> Settings -> Install 二.搜索TensorFlow -> In ...

  4. RabbitMQ入门教程(六):路由选择Routing

    原文:RabbitMQ入门教程(六):路由选择Routing 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...

  5. 利用bing图片搜索接口开发图片搜索应用程序

    概述:通过bing的图片搜索引擎,开发自己的图片搜索应用程序.bing的图片搜索接口是收费的,但是初次注册使用,key可以免费试用30天 程序运行效果如下 一,代码如下 static SearchRe ...

  6. luogu P1852 [国家集训队]跳跳棋

    luogu 直接操作是不可能的,考虑发现一些性质.可以发现如果每次跳的棋子都是两边的,那么最多只有一种方案,那么就把这样操作得到的状态记为当前状态的父亲,从一个状态这样做一定会结束.那么如果两个状态只 ...

  7. 程序员称为高手的10条心得(摘自http://www.jizhuomi.com/software/394.html)

    在这个世界上,有数百万的人热衷于软件开发,他们有很多名字,如:软件工程师(Software Engineer),程序员(Programmer),编码人(Coder),开发人员(Developer).经 ...

  8. spring boot基础学习教程

    Spring boot 标签(空格分隔): springboot HelloWorld 什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新 ...

  9. CentOS 7.4下源码编译安装配置LAMP环境详解

    CentOS 7.4搭建LAMP,LAMP:Linux.Apache.MySQL.PHP. 目录:第一部分 准备工作第二部分 安装Apache服务第三部分 安装MySQL服务第四部分 搭建PHP运行环 ...

  10. PHP判断邮箱地址是否合法的正则表达式

    PHP判断邮箱地址是否合法的正则表达式: function checkEmail($inAddress){ return (preg_match("/^([a-zA-Z0-9_-])+@([ ...