ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

AcWing 190.字串变换

2022-03-04 10:35:31  阅读:169  来源: 互联网

标签:string db 扩展 190 da bfs 字串 size AcWing


很明显是最小步数模型,我们先来分析一波单向起点开始\(bfs\)需要的空间:

假设每次决策数量是 \(K\),那么如果直接\(bfs\),最坏情况下的搜索空间是 \(K^10\),非常大,所以会\(TLE\)或者\(MLE\)。第一层是\(1\),第二层是\(K\),第三层是\(K*K=K^2…..K^10\)

如果采用双向\(bfs\),则可以把搜索空间降到 \(2 \times K^5\)。在实际测试中只需 \(20ms\) 左右,剪枝效果很好。

\(bfs\)的扩展方式是:

  • 枚举在原字符串中使用替换规则的起点
  • 枚举所使用的的替换规则。

双向\(bfs\)的一个优化:在双向\(bfs\)时,每次选择队列中元素数量较少的方向来扩展。

总结:

  • 一边扩展完了另一边还能扩展,说明不连通,达不到终状态
  • 在枚举能替换的状态的时候用substr函数可以方便很多
  • 写代码的时候压入队列扩展写一份即可,从起点扩展的方式和从终点扩展的方式是反过来的,一个是\(a\)变化到\(b\),一个是变化\(b\)变化到\(a\)。
#include <bits/stdc++.h>

using namespace std;
const int INF = 0x3f3f3f3f;
//双向bfs a<->b
//每次需要扩展出一层,不能只扩展出一个,时间复杂度可以优化  k^10-> k^5 *2
//大概估计一下,如果单向容易超时,再改双向尝试。
const int N = 6;
int n;
string a[N], b[N];
int Min = INF; //最短步数
// 功能:取出队头元素,进行n种规则扩展
// q :需要扩展的队列
// da:到起点的距离
// db:到终点的距离
// a :转换前的字符串
// b :转换后的字符串
//返回值:true false,本轮扩展,是否找到了最短步数
bool extend(queue<string> &q, unordered_map<string, int> &da, unordered_map<string, int> &db,
            string a[N], string b[N]) {
    // 取出队头元素
    string t = q.front();
    q.pop();

    for (int i = 0; i < t.size(); i++) // 出发串的每个字符
        for (int j = 0; j < n; j++)    // 枚举规则
            //如果t这个字符串的一段= 规则,比如= xyz,才可以替换
            if (t.substr(i, a[j].size()) == a[j]) {
                // 变换之后的结果r:前面不变的部分+ 变化的部分 + 后面不变的部分
                // 比如abcd ,根据规则abc--> xu,变成 xud,这里的r就是xud
                string r = t.substr(0, i) + b[j] + t.substr(i + a[j].size());
                // r状态是否落到b里面去,两个方向会师,返回最小步数
                if (db.count(r)) {
                    Min = da[t] + 1 + db[r];
                    return true;
                }
                // 如果该状态之前已扩展过,
                if (da.count(r)) continue;
                //没有扩展过,需要加入队列
                da[r] = da[t] + 1;
                q.push(r);
            }
    return false;
}
// 从起点和终点来做bfs
void bfs(string A, string B) {
    if (A == B) {
        Min = 0;
        return;
    } //加入特判,一样了还变换个啥

    queue<string> qa, qb;              //两个方向的队列
    unordered_map<string, int> da, db; //每个状态到起点的距离da(哈希表),每个状态到终点的距离db哈希表

    // qa从起点开始搜,qb从终点开始搜
    qa.push(A), da[A] = 0; // 起点A到起点的距离为0
    qb.push(B), db[B] = 0; // 终点B到终点B的距离为0

    while (qa.size() && qb.size()) {
        bool finish = false;
        // 哪个方向的队列的长度更小一些,空间更小一些,从该方向开始扩展,时间复杂度比较平滑,否则有1个点会超时
        if (qa.size() <= qb.size())
            finish = extend(qa, da, db, a, b);
        else
            finish = extend(qb, db, da, b, a); //以b为起点理解,那么da(终点),db(起点)的概念是需要转化的,同理规则也需要b,a转化

        //扩展后找到最小步数就结束
        if (finish) return;
    }
}

int main() {
    string A, B;
    cin >> A >> B;
    // 读入扩展规则,分别存在a数组和b数组
    // 本题没有给出规则的个数,需要我们用while(cin>>str)读取,vscode中以ctrl+z结束输入。
    while (cin >> a[n] >> b[n]) n++; // n记录的是规则数
    bfs(A, B);

    if (Min > 10)
        puts("NO ANSWER!");
    else
        cout << Min << endl;
}

标签:string,db,扩展,190,da,bfs,字串,size,AcWing
来源: https://www.cnblogs.com/littlehb/p/15963185.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有