《算法竞赛进阶指南》字串变换

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

A1→B1A1→B1

A2→B2A2→B2

规则的含义为:在 AA 中的子串 A1A1 可以变换为 B1B1、A2A2 可以变换为 B2…B2…。

例如:AA=abcd BB=xyz

变换规则为:

abc →→ xu ud →→ y y →→ yz

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

abcd →→ xud →→ xy →→ xyz

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

输入格式

输入格式如下:

AA BB
A1A1 B1B1
A2A2 B2B2
… …

第一行是两个给定的字符串 AA 和 BB。

接下来若干行,每行描述一组字串变换的规则。

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

输出格式

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

输入样例:

abcd xyz
abc xu
ud y
y yz

输出样例:

3

 此题用单向搜索会tle所以需要双向广搜,为何双向广搜时间复杂度会减低?
{
    若用单向广搜,假设每次扩展a步,共需扩展b层,则复杂度为a的10次方
    若用双向广搜,则工序扩展(a的b / 2次方) / 2;
    所以单向广搜是双向广搜的(a的b / 2)/ 2倍(用单向时间复杂度除以双向时间复杂度)
    也可以画图理解:
    如图:

红色区域表示单搜,黑色表示双搜

}

 从终点点往起点,和从起点往终点同时搜,每次搜一层,为什么不是一个点一个点搜?
而是一层一层搜? 如图:

如上图,如果每次不是扩展完整一层,而是只扩展一个点。此时上面该扩展点 aa 了,点 aa 搜到了下半部分的点 cc,此时算出的最短路长度是 x+1+y+1+1=x+y+3x+1+y+1+1=x+y+3。但是最优解可能是后面还没扩展到的点 bb 和点 dd 之间的路径,这条路径的长度是 x+1+y+1=x+y+2x+1+y+1=x+y+2。

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>

using namespace std;

const int N = 6;

int n;
string a[N], b[N];
string start, last;

int extend(queue<string> &qa, unordered_map<string, int> &da, unordered_map<string, int> &db, string a[N], string b[N])
{
    int d = da[qa.front()];//一层一层搜
    while (qa.size() && da[qa.front()] == d)
    {
        auto t = qa.front();
        qa.pop();
        
        for (int i = 0; i < n; i ++ )
            for (int j = 0; j < t.size(); j ++ )
            {
                if (t.substr(j, a[i].size()) == a[i])//若可以改变
                {
                    string res;
                    res += t.substr(0, j) + b[i] + t.substr(j + a[i].size());
                    if (db.count(res)) return da[t] + db[res] + 1;//若在另一个队列中出现过,说明找到了
                    if (da.count(res)) continue;//否则在当前队列出现过,不必重复入队
                    da[res] = da[t] + 1;
                    qa.push(res);
                }
            }
    }
    
    return 11;
}

int bfs(string st, string ed)
{
    if (st == ed) return 0;
    
    unordered_map<string, int> da, db;
    queue<string> qa, qb;
    
    da[st] = 0, db[ed] = 0;
    qa.push(st), qb.push(ed);
    
    int step = 0;
    while (qa.size() && qb.size())
    {
        int t;
        if (qa.size() <= qb.size()) t = extend(qa, da, db, a, b);
        else t = extend(qb, db, da, b, a);
        
        if (t <= 10) return t;
        if (++ step == 10) return -1;
    }
    
    return -1;//当其中一个队列为空而另一个不为空时,说明他们到不了相同状态,
    //因为如果他们可以到相同状态,说明空的队列比可以变化成不空的数列中的一种状态
}

int main()
{
    cin >> start >> last;
    while (cin >> a[n] >> b[n]) n ++ ;
    
    int t = bfs(start, last);
    if (t == -1) puts("NO ANSWER!");
    else printf("%d\n", t);
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啥也不会hh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值