給出句子的加密規則:
- 字母全部變成小寫
- 反轉每個單詞
- 移除所有空格。
現在給定一個加密後的句子,一個可用的單詞表,輸出任意一個加密前的句子,保證有解。
把單詞小寫後反轉插入到AC自動機中,現在只需要把文本串拆分成模式串。
用表示能否被拆分完畢,將文本串輸入到AC自動機中,輸入到位置時枚舉所有匹配的文本串,如果有一個能轉移到,就記錄這個匹配。
最後按轉移輸出句子即可。
AC自動機構建複雜度,匹配複雜度,總複雜度左右。
很經典的AC自動機題目.
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 1000016, MOD = 1000000007;
int dp[M], tr[M];
string pat[M]; //模式串
struct Aho_Corasick_Automaton
{
//Trie樹(圖)/是否爲終結點/失配鏈接/後綴鏈接,都是節點的屬性
int sz = 0;
int ch[M][26], end[M], fail[M], last[M];
int ans[M]; //多模式匹配,終結節點的出現次數
int init(int id = 0)
{
memset(ch[id], 0, sizeof(ch[0]));
end[id] = fail[id] = last[id] = ans[id] = 0;
return sz = id;
}
// 向Trie樹中嘗試插入一個模式串, 返回插入後的節點編號
void insert(const char *s, int id)
{
//printf("ac insert %s\n",s );
int u = 0;
for(int i = 0; s[i]; i++)
{
int &v = ch[u][s[i] - 'a'];
if(!v) v=init(sz+1);
u = v;
}
end[u] = id;
}
// 構建AC自動機
void build()
{
queue<int> q;
for(int v:ch[0]) if(v)
fail[v] = 0, q.push(v);
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = 0; i < 26; i++)
{
int &v = ch[u][i];
if(v)
{
q.push(v);
fail[v] = ch[fail[u]][i];
last[v] = end[fail[v]] ? fail[v] : last[fail[v]]; //後綴鏈接
}
else v = ch[fail[u]][i]; //建立trie圖
}
}
}
// 查詢一個文本串中各終結節點出現了幾次, 保存在ans中
void query(const char *s)
{
int now = 0;
for(int i = 0; s[i]; i++)
{
now = ch[now][s[i] - 'a'];
int id = end[now] ? now : last[now];
while(id)
{
if(dp[i+1-pat[end[id]].size()])
{
tr[i+1] = end[id];
dp[i+1] = 1;
break;
}
id = last[id];
}
}
}
}AC;
char tex[M];
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
memset(tr, -1, sizeof(tr));
int n = read(); //文本串長度
scanf("%s", tex);
int ps = read();
for(int i=1; i<=ps; ++i)
{
cin >> pat[i];
string tst = pat[i];
for(auto &c:tst) c = tolower(c);
reverse(tst.begin(), tst.end());
AC.insert(tst.c_str(), i);
}
dp[0] = 1;
AC.build(); AC.query(tex);
// for(int i=0; i<=n; ++i)
// printf("i=%d dp=%d tr=%d\n",i,dp[i],tr[i] );
stack<int> trs;
for(int i=n; i; i-=pat[tr[i]].size())
{
//printf("i=%d tr=%d\n",i,tr[i] );
trs.push(tr[i]);
}
while(trs.size())
{
cout << pat[trs.top()] << " ";
trs.pop();
}
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}