1032 字串变换
开学还有两天,我却没有努力补作业,在这里刷题…
开学之前练练一些78的搜索剪枝、迭代加深,启发式等
还有一些图论还没有结业,今天全部结束,然后明天搞搞作业准备开学
给定两个字符串
通过一系列的操作进行更改,貌似和搜索并没有关系?
解决这道题可以用迭代加深搜索,正常情况,dfs会超时,可以用迭代加深,就好了
这是第一种做法,做完这个我再来练习剪枝优化,毕竟我是来练习剪枝的
或者,双向广搜
迭代
普通的dfs要一路搜索到底,一路走南墙,可能会在错误答案上一路走到黑,从而浪费大量的时间
迭代加深搜索是什么
就是每次探测一个可能的答案,超过答案就会直接终止搜索,通过枚举答案一层一层的搜索,这样就既有了深搜的递归形式,又有了广搜的一层一层枚举,不会浪费时间,所以迭代加深有价值BDFS,结合了两个搜索的优点
算是比较模板了,需要弄多个剪枝优化
-------------重新---------------------------
嘿嘿嘿
我又来了兄弟们
这个题不就是一个递归搜索吗
显然
我忽略了这个题的本质
我忽略了这个题的颜色
所以说,我总感觉奥赛这个东西其实就是为时间复杂度来服务的
要是没有什么时间复杂度的限制,谁去搞这么麻烦的算法
先说一下题意
题意:
其实就是不停的进行转换
给定两个字符串 A串B串
然后给定几个等价交换的规则,A串换成B串的规则
最后问最快A ——>B的步骤是多少
分析:
典型的搜索
很明显
首先,循环输入,之前已经接触过了,存储到数组里面
双方向的搜索,省时间,每个搜索只需要搞一半
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<map>
using namespace std;
map <string,bool> ed;
map <string,int> st;
string a,b;
string change[30][3];
int t=1,k=2;
int ans=999999;
void dfs(string now,int step)
{
if(step>k)return;//超出步数直接终止,小小的优化
if(now==b)
{
ans=min(ans,step);//搜索到头,比较答案
return;
}
if(ed[now])//判断重复状态
{
if(step>=st[now])return;
}
ed[now]=1;st[now]=step;//记录层数
int loc=0;string changed;
for(int i=1;i<=t;++i)
{//找所有的字符串
loc=-1;//记录位置
while(1)
{//找到了
loc=now.find(change[i][0],loc+1);
if(loc==-1)break;//查找失败
changed=now;
changed.erase(loc,change[i][0].size());
changed.insert(loc,change[i][1]);
dfs(changed,step+1);//查找成功,更改插入
}
}
return;
}
int main()
{
cin>>a>>b;
while(cin>>change[t][0]>>change[t][1])
{
t++;//记录个数
}
t--;
while(ans==999999)//一直无解
{
dfs(a,0);
ed.clear();
st.clear();
k++;
if(k==11)break;//记录搜索次数,小剪枝
}
if(ans==999999)
{
cout<<"NO ANSWER!"<<endl;//答案错误
return 0;
}
cout<<ans<<endl;//最后输出答案
return 0;
}
双向广搜
首先假设我们从起点搜索到终点,直到搜索到s点的时候结束,然后我们再将冗余的部分减去
我们再反向搜索一下,将终点作为起点,起点作为终点搜索一下,再将冗余的部分减去
最后
从一边的节点一定可以顺着另外一边的搜索枝到达终点
那么就实现了双向搜索了
在这个题中,可以使用map去实现查找现在我们搜索的点是否出现过,假如是对面的,那么一位双向的BFS碰面了,结束就好
每次出队的时候,只能够出上一层的搜索节点,不能使用这一层的搜索节点,保证两边一步一步的走
所以map对应的层数应该减去1
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<queue>
using namespace std;
string a[10],b[10],s,t;
int n=1;
map <string,int> A,B;//pair< string , step + 1 >
queue <string> A_,B_;
inline void initialization()
{
cin>>s>>t;
while(cin>>a[n]>>b[n])n++;
}
inline int bfs()
{
int step=0;
A_.push(s);//开始状态
A[s]=0;//开始状态的层数
B_.push(t);//开始状态
B[t]=0;//结束状态的层数
string s,s2;
while(++step<=5)//一边是10步之内,那么两边一起走就是5步之内
{
while(A[A_.front()]==step-1)//保证是上一层的
{
s=A_.front();
A_.pop();
for(int i=1;i<=n;i++)//对于每一个转换方案遍历
{
unsigned int pos=0;//遍历开始搜索的节点,结合string::find( key_string , starting )
while(pos<s.length())
{
if(s.find(a[i],pos)==s.npos)break;//如果找不到了
s2=s;
s2.replace(s2.find(a[i],pos),a[i].length(),b[i]);//replace( starting , length , substitution )
if(A.find(s2)!=A.end())//这棵树里面之前出现过
{
pos++;
continue;
}
if(B.find(s2)!=B.end())return step*2-1;//对面的搜索树里面出现过,由于是上面先走,所以-1
A_.push(s2);//入队
A[s2]=step;
pos++;
}
}
}
while(B[B_.front()]==step-1)//保证是上一层的
{
s=B_.front();
B_.pop();
for(int i=1;i<=n;i++)//对于每一个转换方案遍历
{
unsigned int pos=0;//遍历开始搜索的节点,结合string::find( key_string , starting )
while(pos<s.length())
{
if(s.find(b[i],pos)==s.npos)break;//如果找不到了
s2=s;
s2.replace(s2.find(b[i],pos),b[i].length(),a[i]);//replace( starting , length , substitution )
if(B.find(s2)!=B.end())//这棵树里面之前出现过
{
pos++;
continue;
}
if(A.find(s2)!=A.end())return step*2;//对面的搜索树里面出现过,由于是上面先走,所以-1
B_.push(s2);//入队
B[s2]=step;
pos++;
}
}
}
}
return -1;
}
int main()
{
initialization();
int ans=bfs();
if(ans==-1)
printf("NO ANSWER!");
else printf("%d",ans);
return 0;
}
事实证明双向广搜时间更快,快的不是一点两点