AcWing 190. 字串变换

目录:算法日记

题目来源:190. 字串变换 – AcWing题库

题目描述

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

A1B1A1→B1

A2B2A2→B2

规则的含义为:在 AA 中的子串 A1A_1 可以变换为 B1B_1A2A_2 可以变换为 B2B_2

例如:AAabcd BBxyz

变换规则为:

abc → xu ud → y y → yz

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

abcd → xud → xy → xyz

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

输入格式

A BA B
A1 B1A1 B1
A2 B2A2 B2
… …

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

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

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

输出格式

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

输入样例:

abcd xyz
abc xu
ud y
y yz
复制代码

输出样例:

3
复制代码

算法思路

字符串变换规则最多66种,求最少变换次数,标准最短路模型。虽然字符串变换规则有66种,但可转移的状态数量可能远大于66种。例如,若存在规则ab->aba->b,则对于形如...ababa...的序列,由于存在abba的交替重复出现,显著增加了计算的复杂度。即使在题目要求1010步以内才是有效答案,时间复杂度也高达O(N10)O(N^{10}),其中NN为可扩展状态数。因此引入双向BFS进行优化,初状态与末状态同时BFS,判断是否存在相同的中间状态,时间复杂度可降到O(2N5)O(2N^5)

在进行双向BFS的过程中,可进一步进行优化,优先扩展队列长度较短一侧状态。针对本题需要考虑边界:

  1. 一侧的队列先空,则说明出状态与末状态见不可达;
  2. 总遍历次数大于1010,则说明不存在1010步内的可行解;
  3. 每次BFS需遍历一层状态

针对第33点,需做特殊说明,如下图所示:

未命名文件(7).png

当一侧BFS按状态ABCA、B、C顺序进行入队时,若不按层遍历,则转换经历状态AGDA、G、D共进行44次操作,此时,该解的操作次数小于1010,是一个可行解,但不一定满足最优解。上图中,最优解经历状态CFC、F,仅需进行33次操作。

AC代码

#include<iostream>
#include<cstring>
#include<queue>
#include<map>
using namespace std;
int n = 0;

int extend(queue<string>& q, map<string, int>& da, map<string, int>& db, string ra[], string rb[]) {
    int d = da[q.front()];
    while(!q.empty() && da[q.front()] == d) {
        string cur = q.front();
        q.pop();
        
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < cur.size(); ++j) {
                if(cur.substr(j, ra[i].size()) == ra[i]) {
                    string r = cur.substr(0, j) + rb[i] + cur.substr(j + ra[i].size());
                    if(db.count(r)) return db[r] + 1 + da[cur];
                    if(da.count(r)) continue;
                    da[r] = da[cur] + 1;
                    q.push(r);
                }
            }
        }
    }
    return 11;
}

int bfs(string a, string b, string ra[], string rb[]) {
    if(a == b) return 0;
    map<string, int> da, db;
    queue<string> qa, qb;
    
    qa.push(a);
    da[a] = 0;
    qb.push(b);
    db[b] = 0;
    
    int step = 0;
    while(!qa.empty() && !qb.empty()) {
        int t = 0;
        if(qa.size() < qb.size()) t = extend(qa, da, db, ra, rb);
        else t = extend(qb, db, da, rb, ra);
        
        if(t <= 10) return t;
        if(++step == 10) return -1; 
    }
    return -1;
}

int main() {
    string a, b;
    string ra[6], rb[6];
    cin>>a>>b;
    while(cin>>ra[n], cin>>rb[n]) n++;
    int t = bfs(a, b, ra, rb);
    if(t == -1) cout<<"NO ANSWER!"<<endl;
    else cout<<t<<endl;
    return 0;
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享