Codeforces633C Spy Syndrome 2 (单词Trie)

题目链接: Spy Syndrome 2

大致题意

给定一个长度为n的字符串str, 全部由小写字母组成.
接下来给出单词列表, 共包含m个单词, 每个单词由大写和小写字母组成.

字符串str可以保证是由若干个单词组成, 但是进行了加密.
加密操作: 每个单词的所有字母都被转换成小写字母, 并且进行了翻转.

要求你复原出str, 并且在单词与单词之间加上空格输出.

解题思路

本题不推荐看其他用Trie + dfs的题解, 理论上复杂度应该是爆炸了.

网上深搜的做法都是把单词翻转存入Trie, 但是我反过来把str翻转就T59了.

既然翻转str能T59, 那么把Test59的所有数据翻转输入, 理论上也可以T掉网上的dfs代码.


字典树

考虑字符串str中的每个单词都被翻转过, 因此我们不妨考虑把str进行翻转. 这样我们只需要检查str能否由若干个单词拼出即可.

这里相当于查找某个字符串是否已经出现过, 我们可以想到用Trie树来维护.

考虑到单词有大小写, 而str只有小写. 因此我们不妨对单词进行小写转换, 存入Trie中. 对于每个单词结束的位置, 进行标记.


接下来就是我们怎么去拆分str.

我的做法是: 维护一个dp数组, dp[i]存储字符串的前i个字符是否可以被拆成若干个单词表示, 若可以, 则记录当前位置为结束位置的单词编号(记录路径).

例如: 有长度为10的str = “abcdefghij” 单词有: ab fghij fau cde, 依次编号1~4.

那么 dp[2] = 1, dp[5] = 4, dp[10] = 2. 其余dp[i] = 0.

维护dp的方法: 首先我们对于str进行一次匹配, 把所有有单词结尾的位置进行标记(如上例, 我们会标记出dp[2] = 1). 然后进行每个字符位置遍历: 如果当前位置index可以成功匹配, 则对于index+1的位置再次进行匹配.

这样, 最坏的情况下是每一个位置都被标记, 并且每一个位置都能匹配到Trie树末端. 由于str最长的长度为1E4, 而单词的最大长度为1E3, 因此最坏的复杂度为1E7.

最后考虑如何输出这些单词, 由于我们dp数组内部记录了以第i个位置作为结束的字符串编号, 因此我们可以倒推出所有单词.


代码细节: string的下标从0开始, 而我的dp编号从1开始, 请大家注意区分.

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10, M = 1E6 + 10;
int t[M][26], ind;
int pos[M]; // 对应的字符串编号。

int n; string str;
string text[N]; //所有单词
void insert(const string& s, int id) {
	int x = 0;
	for (auto& op : s) {
		char c = tolower(op) - 'a';
		if (!t[x][c]) t[x][c] = ++ind;
		x = t[x][c];
	}
	pos[x] = id;
}

int dp[N];
void ask(int index) {
	int x = 0;
	for (int i = index; i < n; ++i) {
		char c = str[i] - 'a';
		if (!t[x][c]) return;
		x = t[x][c];

		if (pos[x]) dp[i + 1] = pos[x];
	}
}

vector<int> v;
void dfs(int now = n) {
	if (!now) return;
	v.push_back(dp[now]);
	dfs(now - text[dp[now]].size());
}
int main()
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);

	cin >> n >> str;
	reverse(str.begin(), str.end());

	int m; cin >> m;
	rep(i, m) {
		cin >> text[i];
		insert(text[i], i);
	}

	ask(0);
	rep(i, n) {
		if (!dp[i]) continue;
		ask(i); 
	}
    
	dfs();
	for (auto& op : v) cout << text[op] << ' ';
	puts("");

	return 0;
}

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

逍遥Fau

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

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

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

打赏作者

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

抵扣说明:

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

余额充值