21.10.10模擬 字串
阿新 • • 發佈:2021-10-11
給定一個長度為n的字串,串中的字元保證是前k個小寫字母。你可以在字串後再新增m個字元,使得新字串所包含的不同的子序列數量儘量多。當然,前提是隻能新增前k個小寫字母。求新的長度為n+m的串最多的不同子序列數量。答案對1e9+7取模。
一直想不明白怎麼加串。。後來發現對於要加的直接貪心找每種字元加上後的最優結果。
怎麼求本質不同的子序列呢?
我們可以設last_i為字元s[i]上一次出現的位置,f_i為第i為結尾新出現的不同子序列數量
考慮新加入一個i,那麼第x位結尾
\(last[i] \le x \le i-1\) 新出現的子序列都可以加上i得到一個新的
怎麼樣證明這樣不會產生重複的子序列呢
反證法
如果如果以last[i] − 1之前的某個位置為結尾的子序列接上第i位,會產生一個新的子序列,那麼之前的子序列直接加上last[i] 也可以產生這個新的子序列。
這與這個子序列在i第一次出現矛盾
\(f[i]=\sum^{i-1}_{j=last[i]} f[j]\)
字首和優化
f[i]表示列舉到第i位所有本質不同子序列數量,考慮第i位的數字a[i]要不要加
如果a[i]沒出現過,那麼f[i]就是相當於比f[i-1]多了一倍,而且還多了a[i]這個長度為1的子序列
如果a[i]出現過,那麼就容斥減去
rep(i, 2, len) { if(!last[a[i]]) f[i] = 1ll * f[i - 1] * 2 % mod + 1; else f[i] = 1ll * f[i - 1] * 2 % mod - f[last[a[i]] - 1]; if(f[i]<mod) f[i]+=mod; if(f[i]>mod) f[i]%=mod; last[a[i]]=i; }
本題就是後面m個多了個列舉字元。
int m, k, ans = 1, n; int f[N], last[N]; int main() { freopen("string.in", "r", stdin); freopen("string.out", "w", stdout); cin >> m >> k; char ch; while(cin >> ch) { int x = ch - 'a' + 1; if(x < 1 || x > 26) break; int tmp = ans; ans = (ans*2%mod - f[x] + mod) % mod; f[x] = tmp; last[x] = ++n; } rep(i, 1, m) { int x(0); rep(j, 1, k) { if(!x || last[x] > last[j]) x = j; //Ì°ÐÄÕÒ×îÇ°ÃæµÄ } last[x] = ++n; int tmp = ans; ans = (ans*2%mod - f[x] + mod) % mod; f[x] = tmp; } cout << ans << '\n'; return 0; }
本文來自部落格園,作者:{2519},轉載請註明原文連結:https://www.cnblogs.com/QQ2519/p/15392312.html