[NOIP 2002 提高组] 字串变换(疑似错题)

GitHub跳转原题关系图返回列表

把字符串作为 BFS 状态,枚举每条规则的所有出现位置,求十步内到目标串的最少变换数。

OJ: luogu

题目 ID: P1032

难度:普及+/提高

标签:BFS字符串最短路python

日期: 2026-07-16 18:01

题意

给定起始串、目标串和若干替换规则。一次操作可把当前串中某次出现的左串替换为右串,求十步以内的最少操作数,否则输出 NO ANSWER!

思路

把每个字符串看成图上的状态,一次合法替换是一条权值为一的边。要求最少变换次数,因此从起始串 BFS。

对每条规则,用 str.find(old,start) 循环寻找所有出现位置,包括可能重叠的出现;字符串切片构造替换后的新状态。集合 visited 保证同一字符串只入队一次。深度达到十步后不再扩展。

Python 知识

  • 字符串不可变,current[:pos]+new+current[pos+len(old):] 会安全生成新状态。
  • 字符串本身可哈希,可以直接放进 set 判重。
  • sys.stdin.read().splitlines() 适合读取“首行固定、后续规则直到 EOF”的格式。
  • /home/rainboy/mycode/hugo-blog/content/program_language/python/oj_input_output_cheatsheet.md:整行字符串与 EOF 输入。
  • /home/rainboy/mycode/hugo-blog/content/program_language/python/bfs_shortest.md:字符串状态图 BFS。

代码

python
import sys
from collections import deque


lines = [line.split() for line in sys.stdin.read().splitlines() if line.strip()]
start, target = lines[0]
rules = lines[1:]
queue = deque([(start, 0)])
visited = {start}
answer = None

while queue:
    current, steps = queue.popleft()
    if current == target:
        answer = steps
        break
    if steps == 10:
        continue

    for old, new in rules:
        position = current.find(old)
        while position != -1:
            transformed = current[:position] + new + current[position + len(old):]
            if transformed not in visited:
                visited.add(transformed)
                queue.append((transformed, steps + 1))
            position = current.find(old, position + 1)

print(answer if answer is not None else "NO ANSWER!")
cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-07-27 00:00
 * update_at: 2026-07-27 00:00
 */

/* P1032 [NOIP 2002 提高组] 字串变换 */
/* BFS 求十步内从起始串到目标串的最少变换次数。 */

#include <bits/stdc++.h>
using namespace std;

string start_str, target;
string from[10], to[10]; // 替换规则
int rule_cnt;            // 规则数量

// BFS 状态:当前字符串
struct Node {
    string s;
    int step;
};

int bfs() {
    queue<Node> q;
    set<string> vis;
    q.push({start_str, 0});
    vis.insert(start_str);

    while (!q.empty()) {
        Node cur = q.front();
        q.pop();

        if (cur.s == target) return cur.step;
        if (cur.step == 10) continue; // 超十步不考虑

        // 尝试每条规则
        for (int r = 0; r < rule_cnt; r++) {
            string& old_s = from[r];
            string& new_s = to[r];
            size_t pos = cur.s.find(old_s);

            // 一个规则可能有多个匹配位置
            while (pos != string::npos) {
                string next = cur.s;
                next.replace(pos, old_s.length(), new_s);

                if (vis.find(next) == vis.end()) {
                    vis.insert(next);
                    q.push({next, cur.step + 1});
                }

                // 继续向后查找
                pos = cur.s.find(old_s, pos + 1);
            }
        }
    }
    return -1; // 不可达
}

int main() {
    cin >> start_str >> target;
    string a, b;
    while (cin >> a >> b) {
        from[rule_cnt] = a;
        to[rule_cnt] = b;
        rule_cnt++;
    }

    int ans = bfs();
    if (ans == -1)
        cout << "NO ANSWER!\n";
    else
        cout << ans << "\n";

    return 0;
}

复杂度

状态数量最坏呈指数增长;设十步内实际访问 V 个字符串,每个状态枚举规则和出现位置的代价为 T,总时间为 O(VT)O(VT),空间为 O(V)O(V)

总结

字符串变换的关键是把“当前完整字符串”作为状态。BFS 保证第一次到达目标就是最少步数,十步限制控制搜索深度。