题目链接:点击打开链接
题意:给你一个m个单词的字典, 给你一句话, 这句话中的每个单词都来自字典, 字典中的每一个单词可重复使用, 把这句话的所有大写字母变成小写字母, 然后反转后去掉空格。 要求你恢复这句话。
思路:一看就是一个典型的DP, 很容易想到, 枚举每个单词的起点, 然后向后找这个单词的终点, 并转移过去。 但是问题在于, n和m都很大, n*1000复杂度如果再用map查找将会超时, 所以可以用trie树顺便维护。 复杂度O(n * 1000)
细节参见代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
typedef long double ld;
const ld eps = 1e-9, PI = 3.1415926535897932384626433832795;
const int mod = 1000000000 + 7;
const int INF = int(1e9);
// & 0x7FFFFFFF
const ll INF64 = ll(1e18);
const int maxn = 100000 + 10;
const int maxm = 1000000 + 10;
const int seed = 131;
int T,n,m,nxt[maxm][36],id[maxm],d[maxn], root, sz;
string table[maxn], s;
int newNode() {
id[sz] = -1;
memset(nxt[sz], -1, sizeof(nxt[sz]));
return sz++;
}
void init() {
sz = 0; // 下一个结点值
root = newNode(); // 创建新结点
}
void update(string& s, int idx) {
int u = root; //根结点
for(int i=s.size() - 1; i >= 0; --i) {
int c = tolower(s[i]) - 'a';
int& v = nxt[u][c];
if(v == -1) v = newNode();
u = v;
}
id[u] = idx;
}
void query(int idx) {
int u = root;
for(int i=idx;i<n;i++) {
u = nxt[u][s[i]-'a'];
if(u == -1) return;
if(id[u] != -1 && !d[i+1]) {
d[i+1] = id[u];
}
}
}
int main() {
ios_base::sync_with_stdio(false);
cin>>n>>s>>m;
init();
for(int i=1;i<=m;i++) {
cin >> table[i];
update(table[i], i);
}
d[0] = 1;
for(int i=0;i<n;i++) {
if(!d[i]) continue;
query(i);
}
vector<int> ans;
int u = n;
while(u) {
ans.push_back(d[u]);
u -= table[d[u]].size();
}
for(int i=ans.size()-1;i>=0;--i) {
cout << table[ans[i]] << (i == 0 ? '\n' : ' ');
}
return 0;
}