每日一題之 hiho227周 Longest Subsequence
描述
You are given a set of N words W1, W2, … WN and a target string S. Find the longest word which is also a subsequence of S.
輸入
The first line contains an integer N. (1 <= N <= 10000)
The following N lines each contain a word Wi. (1 <= Σ|Wi| <= 100000)
The last line contains the string S. (1 <= |S| <= 100000)
輸出
The length of the longest word which is also a subsequence of S.
樣例輸入
3
a
ac
abc
acbc
樣例輸出
3
題意:
給n個詞,以及一個目標字串S,輸出這n個詞中是S的子序列且詞長度最大的那個詞的長度。這裡要注意的是子序列不要求連續的,但是如果是子串的話就要求是連續的。
思路:
一般我們判斷字串Wi是不是字串S的子序列,複雜度是O(|S|)的:
我們從左向右從S裡找到一個字元Wi[0],再向右找第一個Wi[1],再向右找第一個Wi[2]…,直到找到Wi最後一個字元或者S結束也沒有找完Wi。那麼Wi就不是S的子序列
不過如果我們能先計算出來f[i][c]表示S[i]之後,第一個字元c的位置,那麼上面的計算過程可以優化到O(|Wi|):
因為一旦我們找到S[i]是第一個Wi[0]之後,再向右的第一個Wi[1]的位置就是f[i][Wi[1]],再向右第一個Wi[2]的位置就是f[ f[i][Wi[1]] ][Wi[2]] …
而f陣列的計算可以通過從後向前掃描S,O(26 * |S|)計算出來。具體的轉移方程是
這裡 j 是代表26個字元中一個。
有了f之後,再依次計算Wi是不是S的子序列,總複雜度是O( Σ|Wi| )的。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 1e5+7;
int f[maxn][30];
void init(string &S) {
int len = S.length();
memset(f,-1,sizeof(f));
for (int i = len-1; i >= 0; --i) {
for (int j = 0; j < 26; ++j) {
if (S[i+1]-'a' == j) {
f[i][j] = i+1;
}
else
f[i][j] = f[i+1][j];
}
}
}
void solve(string &S, vector<string> &words) {
init(S);
int len = words.size();
int slen = S.length();
string word;
int res = 0;
for (int i = 0; i < len; ++i) {
word = words[i];
int tmp = 0;
int wlen = word.length();
int j;
for (j = 0; j < slen; ++j) {
if (word[0] == S[j]) {
tmp = 1;
break;
}
}
if (tmp == 0) continue;
int k = 1;
while(k < wlen && f[j][word[k]-'a'] != -1) {
++tmp;
j = f[j][word[k]-'a'];
++k;
}
if (tmp == wlen)
res = max(res,tmp);
}
cout << res << endl;
}
int main() {
int n;
cin >> n;
vector<string>words;
string S,s;
for (int i = 0; i < n; ++i) {
cin >> s;
words.push_back(s);
}
cin >> S;
solve(S, words);
return 0;
}