洛谷 P1032 字串变换:BFS 高效解法详解

题目分析

问题描述

给定两个字符串 A 和 B,以及一组字符串变换规则(最多 6 个),要求通过应用这些规则将 A 转换为 B。每个规则的形式是 A₁ → B₁,表示可以在 A 中的子串 A₁ 替换为 B₁。目标是在 10 步(含)以内找到最少的变换步数,否则输出 "NO ANSWER!"。

示例分析

示例输入:

abcd xyz
abc xu
ud y
y yz

转换过程:

abcd → xu ud (应用 abc → xu)
xu ud → xy d (应用 ud → y)
xy d → xyz (应用 y → yz)

输出: 3

算法思路

核心思想:广度优先搜索(BFS)

  1. 状态表示:每个状态保存当前字符串和已用步数
  2. 队列处理:使用队列按层遍历所有可能的转换
  3. 剪枝优化
    • 记录已访问的字符串,避免重复处理
    • 限制最大步数为 10
  4. 规则匹配:对当前字符串尝试所有规则,生成新状态

算法步骤

  1. 读取初始字符串 A 和目标字符串 B
  2. 读取所有转换规则
  3. 初始化队列,将初始状态 (A, 0) 加入队列
  4. 开始 BFS:
    • 取出队首状态
    • 如果当前字符串等于 B,返回当前步数
    • 如果步数已达 10,跳过该分支
    • 对每个规则,在当前字符串中查找所有可匹配的位置
    • 生成新字符串,若未访问过则加入队列
  5. 如果队列为空仍未找到解,返回 "NO ANSWER!"

完整AC代码

#include <iostream>
#include <string>
#include <queue>
#include <unordered_set>
#include <vector>
using namespace std;

struct State {
    string str;
    int steps;
    
    State(string s, int st) : str(s), steps(st) {}
};

int main() {
    string A, B;
    cin >> A >> B;
    
    vector<pair<string, string>> rules;
    string a, b;
    while (cin >> a >> b) {
        rules.emplace_back(a, b);
    }
    
    queue<State> q;
    unordered_set<string> visited;
    
    q.push(State(A, 0));
    visited.insert(A);
    
    while (!q.empty()) {
        State current = q.front();
        q.pop();
        
        // 找到目标
        if (current.str == B) {
            cout << current.steps << endl;
            return 0;
        }
        
        // 超过步数限制
        if (current.steps >= 10) {
            continue;
        }
        
        // 应用所有规则
        for (const auto& rule : rules) {
            const string& from = rule.first;
            const string& to = rule.second;
            size_t pos = 0;
            
            // 查找所有匹配位置
            while ((pos = current.str.find(from, pos)) != string::npos) {
                // 生成新字符串
                string newStr = current.str;
                newStr.replace(pos, from.length(), to);
                
                // 如果未访问过
                if (visited.find(newStr) == visited.end()) {
                    visited.insert(newStr);
                    q.push(State(newStr, current.steps + 1));
                }
                
                // 继续查找下一个匹配位置
                pos += from.length();
            }
        }
    }
    
    // 未找到解
    cout << "NO ANSWER!" << endl;
    return 0;
}

代码详解

1. 数据结构设计

struct State {
    string str;
    int steps;
    
    State(string s, int st) : str(s), steps(st) {}
};
  • State结构体:保存当前字符串和已用步数
  • 队列q:用于BFS的状态队列
  • 哈希表visited:记录已处理的字符串,避免重复

2. BFS核心逻辑

while (!q.empty()) {
    State current = q.front();
    q.pop();
    
    // 找到目标
    if (current.str == B) {
        cout << current.steps << endl;
        return 0;
    }
    
    // 超过步数限制
    if (current.steps >= 10) {
        continue;
    }
    
    // 应用所有规则
    for (const auto& rule : rules) {
        // ... 生成新状态 ...
    }
}
  • 终止条件:找到目标字符串或步数超限
  • 规则应用:对每个规则尝试所有可能的替换位置

3. 规则匹配实现

size_t pos = 0;
while ((pos = current.str.find(from, pos)) != string::npos) {
    string newStr = current.str;
    newStr.replace(pos, from.length(), to);
    
    if (visited.find(newStr) == visited.end()) {
        visited.insert(newStr);
        q.push(State(newStr, current.steps + 1));
    }
    
    pos += from.length();
}
  • 查找所有匹配:使用find函数循环查找
  • 替换操作:使用string::replace方法
  • 去重处理:只有未访问过的状态才加入队列

关键优化点

1. 剪枝策略

  • 步数限制:严格控制在10步以内
  • 状态去重:使用哈希表记录已访问状态

2. 字符串处理

  • 高效替换:利用string的内置方法
  • 全位置匹配:确保不遗漏任何可能的替换位置

复杂度分析

时间复杂度

  • 最坏情况:O(6^10)(6条规则最多应用10次)
  • 实际运行:由于剪枝和题目数据水,可以通过测试

空间复杂度

  • O(N):N为不同字符串状态的数量

测试用例验证

样例输入1

abcd xyz
abc xu
ud y
y yz

输出:

3

样例输入2

aaaaa bbabb
ab aab
ba baa
aa aaa
aaa aa
a aa

输出:

4

常见错误与解决

1. 超时问题

原因:未进行有效剪枝,导致搜索空间过大 解决

  • 严格限制步数不超过10
  • 使用哈希表记录已访问状态

2. 遗漏解

原因:规则应用不彻底,遗漏某些替换位置 解决:确保对每个规则的每个可能匹配位置都进行处理

3. 重复计算

原因:相同状态被多次处理 解决:使用visited集合记录已处理状态

算法总结

本文提供的BFS解法能有效解决洛谷P1032问题,关键优势在于:

  1. 广度优先:保证找到的是最少步数解
  2. 高效剪枝:通过步数限制和状态去重优化性能
  3. 全面搜索:确保不遗漏任何可能的转换路径
  4. 代码健壮:处理了各种边界情况

这种基于状态空间的搜索方法适用于许多字符串转换问题,掌握其核心思想可以解决类似的问题。

  🔥 关注我,解锁CSP-J/S竞赛全攻略 🔥

(每日更新高频考点 + 精选真题解析,助你轻松备赛!)
👇 点击关注立即提升竞赛战力 👇
[https://blog.csdn.net/stillwatersss]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

杨小码不BUG

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

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

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

打赏作者

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

抵扣说明:

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

余额充值