月月查華華的手機 【每日一題】4月2日題目精講 枚舉優化

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章