Loading

Edit Step Ladders - UVA 10029

题意

题目链接(Virtual Judge):Edit Step Ladders - UVA 10029

题意:

如果单词 \(x\) 能通过添加、删除或修改一个字母变换为单词 \(y\),则称单词 \(x\) 到单词 \(y\) 的变换为一个 edit step。

Edit step ladder 指的是一个按字典序排列的单词序列 \(w_1,w_2,\ldots,w_n\),每个 \(w_{i+1}\) 都由 \(w_i\) 经一个 edit step 变换而来。

给出一个按字典序排列的单词序列,问其中符合 edit step ladder 要求的最长子序列的长度。

思路

虽然这题目又是字典序,又是子序列什么的,但其实跟字符串没多少关系,做法是建图跑最长路,将单词视为图节点,单词之间的变换视为有向边。想出建图这个思路后,剩下的就好办了。

首先是怎样建图的问题。如果采用枚举所有单词对的方法,时间复杂度 \(O(n^2)\),很可能会超时。我们注意到单词的长度非常短,所以不妨换一种思路:对于一个单词,枚举它能变换出的所有单词,这样便能实现 \(O(n)\) 建图。

具体做法如下:用哈希表保存单词及其对应下标。对于一个单词,枚举它经过一步 edit step 变换后的所有单词,看变换后在不在哈希表里。如果在,且下标大于当前单词(为了满足字典序要求),则构造一条有向边。

接着是怎样求解最长路的问题。显然此图是个有向无环图,所以我们可以用动态规划的方法 \(O(n)\) 求出最长路。

\(\mathrm{step}(u)\) 表示从 \(u\) 出发的最长路的长度(路径上的节点数),状态转移方程如下:

\[\mathrm{step}(u)=\left\{ \begin{array}{ll} 1 & \text{if}\; u \;\text{is a leaf} \\ 1+\max\{\mathrm{step}(v) \mid (u,v)\in E\} & \text{otherwise} \\ \end{array} \right. \]

代码

注意:输出答案后还要再输出一个换行符,否则 UVA 会判你 WA(而不是 PE),非常的毒瘤。

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <cassert>

using namespace std;

const int maxn = int(25000 + 5);
string word_arr[maxn];
unordered_map<string, int> word_dict;
vector<int> G[maxn];
int step_arr[maxn];

// 将 s 的第 i 个字符换成 ch
string change_letter(const string &s, int i, char ch)
{
    string ret = s;
    ret[i] = ch;
    return ret;
}

// 将 s 的第 i 个字符移除
string remove_letter(const string &s, int i)
{
    string ret;
    int len = int(s.length());
    ret.reserve(len - 1);
    for (int j = 0; j < len; j++)
        if (j != i)
            ret.push_back(s[j]);
    return ret;
}


// 在 s 的第 i 个字符前面插入字符 ch
string insert_letter(const string &s, int i, char ch)
{
    string ret;
    int len = int(s.length());
    ret.reserve(len + 1);
    if (i == len)
    {
        ret = s;
        ret.push_back(ch);
        return ret;
    }
    for (int j = 0; j < len; j++)
    {
        if (j == i)
            ret.push_back(ch);
        ret.push_back(s[j]);
    }
    return ret;
}

// 添加有向边 u -> v
void add_edge(int u, int v)
{
    G[u].push_back(v);
}

// 从点 u 出发的最长路的长度
int longest_step(int u)
{
    if (step_arr[u] > 0)
        return step_arr[u];
    int max_son_step = 0;
    for (int v : G[u])
        max_son_step = max(max_son_step, longest_step(v));
    step_arr[u] = max_son_step + 1;
    return step_arr[u];
}

int main()
{
    ios_base::sync_with_stdio(false);
    string word;
    int index = 1;
    while (cin >> word)
    {
        word_arr[index] = word;
        word_dict[word] = index;
        index++;
    }
    int n = index - 1;

    for (int u = 1; u <= n; u++)
    {
        const string &cur = word_arr[u];
        int len = int(cur.length());
        // 修改字符
        for (int i = 0; i < len; i++)
        {
            for (char ch = cur[i] + 1; ch <= 'z'; ch++)
            {
                string gen = change_letter(cur, i, ch);
                if (word_dict.count(gen) > 0)
                {
                    int v = word_dict[gen];
                    assert(u < v);
                    add_edge(u, v);
                }
            }
        }
        // 移除字符
        for (int i = 0; i < len; i++)
        {
            string gen = remove_letter(cur, i);
            if (word_dict.count(gen) > 0)
            {
                int v = word_dict[gen];
                if (u < v)
                    add_edge(u, v);
            }
        }
        // 插入字符
        for (int i = 0; i <= len; i++)
        {
            for (char ch = 'a'; ch <= 'z'; ch++)
            {
                string gen = insert_letter(cur, i, ch);
                if (word_dict.count(gen) > 0)
                {
                    int v = word_dict[gen];
                    if (u < v)
                        add_edge(u, v);
                }
            }
        }
    }

    int ans = 0;
    for (int u = 1; u <= n; u++)
        ans = max(ans, longest_step(u));
    cout << ans << '\n';

    return 0;
}
posted @ 2021-03-11 17:33  zhb2000  阅读(155)  评论(0编辑  收藏  举报