https://ac.nowcoder.com/acm/problem/23053
這就是個簡單的枚舉優化,不要認爲有多麼的複雜高深(學算法最忌諱自己嚇唬自己,你怕了就已經輸了)。
我們可以觀察,比如主串(華華的暱稱)是abcdefgh子串(小姐姐的暱稱)是 aez,那麼當 a 匹配上了之後,再去枚舉主串中的 bcd 其實是沒有意義的,如果我們能做到在 a 之後跳到 a 後面第一個 e 然後在 e 之後跳到後面的第一個 z(如果有的話),直到子串遍歷完或者跳不動了,就能出結果了。(如果主串中有兩個一樣的字母,我們肯定會選前一個,因爲是子序列,如果選前一個不可以,那麼選後面的同一個字母肯定就更不可以了,因爲選越靠後的字母給後面留下的選擇餘地就越小。)
那麼我們怎麼實現在主串跳着枚舉這個事情呢?
開一個數組next[i][′a′...′z′]表示主串第 i 個字母之後的第一個′a′...′z‘分別在哪一個位置,子串每次與之匹配的時候順着往後走就行。
next數組的維護只需要從後往前掃描主串,在 掃描到第 iii 位的時候始終維護一個數組 last[′a′...′z′] 表示當前位置往後最靠前的字母′a′...′z′分別在哪,然後把這個數組複製給next[i]就可以了。
這樣的話每次匹配的複雜度是O(∣Bi∣)的(只要順着Bi遍歷完就可以判斷是不是子序列了)。總複雜度是O(26∣A∣+∑∣Bi∣)
自己按照題解的要求寫的咋都過不了樣例,心態崩了不管了。。。。
#include<bits/stdc++.h>
using namespace std;
const int N = 1000100;
char a[N], b[N];
int n;
int last[30];
int nxt[N][30];
void deal()
{
memset(last, -1, sizeof(last)); //-1表示不存在
int l = strlen(a);
for (int i = l-1; i >= 0; i--)
{
for (int k = 0; k < 26; k++)
{
nxt[i][k] = last[k]; //將last['a'...'z']複製給當前的next[i]['a'...'z']
}
last[a[i] - 'a'] = i; //當前字母修改last
}
}
bool calc()
{
int l1 = strlen(a);
int l2 = strlen(b);
int i = last[b[0] - 'a']; //i初值直接賦爲A中子串首字母的位置
if (i == -1) return 0; //如果A中沒有這個首字母 直接無解
for (int j = 1; j < l2; j++) //j遍歷子串中的字母(首字母就不必了)
{
i = nxt[i][b[j] - 'a'] ; //i跳到前一個字母后面的當前字母
if (i == -1) return 0;
}
return 1;
}
int main(){
scanf("%s", a);
deal();
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%s",b);
if (calc()) printf("Yes\n");
else printf("No\n");
}
return 0;
}