P1032 字串变换 洛谷 BFS+剪枝+CMP

该博客主要介绍了如何解决洛谷P1032字串变换问题,通过BFS算法配合剪枝策略来找出从字符串A变换到字符串B的最短路径。在过程中提到了常见的错误和陷阱,如子序列判断、AC自动机的使用以及序列可能包含多个子序列的情况,并给出了采用CMP方法通过的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

题目描述

已知有两个字串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。

输入输出格式

输入格式:

 

输入格式如下:

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

... ... /

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

 

输出格式:

 

输出至屏幕。格式如下:

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

 

输入输出样例

输入样例#1: 复制

abcd xyz
abc xu
ud y
y yz

输出样例#1: 复制

3

 

题目看上去简单,其实全是坑,疯狂WA了10多发,真想警告

首先是最简单的BFS,判断每个序列是否是子序列,按道理应该用AC自动机,但是懒得打那么多了,CMP也能过

然后是用map进行剪枝,不剪的话会疯狂TLE,还有一个坑就是序列不一定只含有一个子序列,模板串可能有多个子序列

 

下面直接贴代码

#include <cstdio>  
#include <cstring>  
#include <iostream>  
#include <algorithm>
#include <string>
#include <queue>
#include <map>
using namespace std;
struct MyString{
    char s[250];
    int deep;
    MyString(char *s,int deep){
        strcpy(this->s,s);
        this->deep=deep;
    }
    
};
bool operator<(const MyString &temp1,const MyString &temp2){
        return temp1.deep<temp2.deep;
}
map <string ,int> M;
queue<struct MyString> que;
int ne[250];
int man[20];
int mindex;
void initkmp(char x[], int m) {
    int i, j; j = ne[0] = -1; i = 0;
    while (i < m) {
    while (j != -1 && x[i] != x[j])
    j = ne[j];
    ne[++i] = ++j;
    }
}
int kmp(char x[], int m, char y[], int n) {
    mindex=0;
    int i, j, ans; i = j = ans = 0;
    initkmp (x, m);
    while (i < n) {
        while (j != -1 && y[i] != x[j]) j = ne[j];
        i++; j++;
        if (j >= m) {
            man[mindex++]=i-m; 
        }
    }
    return mindex;
}
char sa[10][250];
char sb[10][250];
char A[25],B[25];
int main(){
    // cin>>sa[index]>>sb[index];
    // cout<<kmp(A,5,B,10)<<endl;
    // cout<<kmp(sa[1],5,sb[1],10)<<endl;
    cin>>A>>B;
    int index=1;
    while(cin>>sa[index]>>sb[index]){
        index++;
        if(index==7) break;
    } 
    index--;
    MyString temp(A,0);
    que.push(temp);
    M[temp.s]=temp.deep;
    while(!que.empty()){
        temp=que.front();
        que.pop();
        if(strlen(temp.s)>200) continue;
        //if(temp.deep==8) cout<<temp.s<<endl;
        if(temp.deep>10){
            cout<<"NO ANSWER!"<<endl;
            return 0;

        }
        if(strcmp(B,temp.s)==0){
            cout<<temp.deep<<endl;
            return 0;
        }
        for(int i=1;i<=index;i++){
            int ret=kmp(sa[i],strlen(sa[i]),temp.s,strlen(temp.s));
            // cout<<sa[i]<<" "<<strlen(sa[i])<<" "<<temp.s<<" "<<strlen(temp.s)<<" "<<ret<<endl;
            // cout<<temp.deep<<endl;
            if(ret){
                for(int ii=0;ii<mindex;ii++){
                    ret=man[ii];
                    char news[250];
                    int l,j;
                    for(l=0;l<ret;l++) news[l]=temp.s[l];
                    for(j=0;j<strlen(sb[i]);j++) news[l++]=sb[i][j];
                    for(int k=ret+strlen(sa[i]);k<=strlen(temp.s);k++) news[l++]=temp.s[k];
                    MyString put(news,temp.deep+1);
                    int countn=M.count(put.s);
                    if(!countn){
                        que.push(put);
                        M[put.s]=put.deep;
                    }
                }
                
                            
            }
        }
    }
    if(que.empty()) cout<<"NO ANSWER!"<<endl;
    return 0;
}




 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值