G. Anthem of Berland - kmp+DP
阿新 • • 發佈:2022-05-08
G. Anthem of Berland
https://codeforces.com/contest/808/submission/156274622
題意
給定兩個字串 s,t, s中可含有'?'問號可以由任何字母代替
將s中的所有'?'用某個字母代替後 s中最多可以匹配多少個t (可以重疊部分)
思路
用dp[i]表示s的前i個字元最多可匹配的t串的數量
用b[i]表示s的前i個位置 第i個位置與t匹配情況下的t出現的最大次數
用nx[]陣列預處理t的最大前後綴匹配長度 因為當有相同前後綴的時候可能會有重疊的t在重疊的情況下可能匹配的更多 轉移的情況就是b[i] = max(b[i], b[i - t.size() + k])了
用動態規劃 實現b陣列和dp陣列填充
#include<bits/stdc++.h> #include<unordered_map> #define ll long long #define ull unsigned long long #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); using namespace std; const ll inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; const double eps = 1e-4; const ll N = 1e6 + 5; const int M = 1e5 + 5; const int mod = 1e9 + 7; ll n, m, q; ll a[N], nx[N], dp[N], b[N]; string s, t; //用kmp預處理nx陣列 void getnx(string s) { ll k = -1, j = 0; nx[0] = -1; while (j < s.size()) { if (k == -1 || s[k] == s[j]) { k++; j++; nx[j] = k; } else k = nx[k]; } } //判斷當前p位置開始能否匹配一個t bool check(ll p) { for (int i = p, j = 0; i < s.size() && j < t.size(); i++, j++) { if (s[i] != t[j] && s[i] != '?') return false; } return true; } void solve() { cin >> s >> t; getnx(t); int flag = 1, cnt = 0, cnt2 = 0, f, fg = 0; if (s.size() < t.size()) { cout << 0 << "\n"; return; } for (int i = 0; i <= s.size() - t.size(); i++) { //先將前一個dp值轉移過來 dp[i + t.size()] = dp[i + t.size() - 1]; //判斷以p為起點能否與t匹配 if (check(i)) { //先將b[i + t.size()]賦值為dp[i]因為一定包含dp[i]的值 b[i + t.size()] = dp[i]; //用nx陣列取出可轉移成b[i + t.size()]的最優情況 for (int k = nx[t.size()]; k >= 0; k = nx[k]) { b[i + t.size()] = max(b[i + t.size()], b[i + k]); } //當前位置可匹配的再加1 b[i + t.size()]++; //取與不取擇優(即是否將i開始的一串匹配成t) dp[i + t.size()] = max(dp[i + t.size()], b[i + t.size()]); } //cout << dp[i + 1] << " "; } //輸出 cout << dp[s.size()] << "\n"; } signed main() { IOS; int t = 1; //cin >> t; while (t--) { solve(); } }