[CTSC 2012] Cheat
[题目链接]
https://www.lydsy.com/JudgeOnline/problem.php?id=2806
[算法]
首先建立广义后缀自动机
注意到问题具有单调性 , 不妨对于每组询问二分答案mid
如何检验?
记fi表示前i个字符最多能选几个 , 有转移方程 :
fi = max{ fi - 1 , fj + i - j } (i - maxlen[i] <= j <= i - mid)
其中maxlen[i]表示第i个字符向前最多可匹配多少个字符
i - maxlen[i]单调递增 , i - mid同样单调递增
单调队列优化即可
时间复杂度 : O(NlogN)
[代码]
#include<bits/stdc++.h> using namespace std; #define N 1100010 int n , m , L; int dp[N] , maxlen[N] , q[N]; char s[N]; #define rint register int struct Suffix_Automaton { int sz , last; int father[N] , child[N][3] , depth[N]; Suffix_Automaton() { sz = 1; last = 1; } inline int new_node(int dep) { depth[++sz] = dep; return sz; } inline void extend(int ch) { int np = new_node(depth[last] + 1); int p = last; while (child[p][ch] == 0) { child[p][ch] = np; p = father[p]; } if (child[p][ch] == np) father[np] = 1; else { int q = child[p][ch]; if (depth[q] == depth[p] + 1) father[np] = q; else { int nq = new_node(depth[p] + 1); father[nq] = father[q]; father[np] = father[q] = nq; memcpy(child[nq] , child[q] , sizeof(child[q])); while (child[p][ch] == q) { child[p][ch] = nq; p = father[p]; } } } last = np; } inline void match() { int now = 1 , mxlen = 0; for (rint i = 1; i <= L; ++i) { int nxt = s[i] - '0'; while (now != 1 && !child[now][nxt]) now = father[now] , mxlen = depth[now]; if (child[now][nxt]) { ++mxlen; now = child[now][nxt]; } else { mxlen = 0; now = 1; } maxlen[i] = mxlen; } } } SAM; inline void chkmin(int &x , int y) { x = min(x , y); } inline void chkmax(int &x , int y) { x = max(x , y); } inline bool check(int mid) { int l = 1 , r = 0; for (rint i = 1; i < mid; ++i) dp[i] = 0; for (rint i = mid; i <= L; ++i) { while (l <= r && dp[q[r]] - q[r] <= dp[i - mid] - i + mid) --r; q[++r] = i - mid; while (l <= r && q[l] < i - maxlen[i]) ++l; dp[i] = dp[i - 1]; if (l <= r) chkmax(dp[i] , dp[q[l]] - q[l] + i); } return dp[L] * 10 >= 9 * L; } int main() { scanf("%d%d" , &n , &m); int mxl = 0; for (rint i = 1; i <= m; ++i) { scanf("%s" , s + 1); L = strlen(s + 1); chkmax(mxl , L); for (rint j = 1; j <= L; ++j) SAM.extend(s[j] - '0'); SAM.extend(2); } for (rint i = 1; i <= n; ++i) { scanf("%s" , s + 1); int l = 1 , r = strlen(s + 1) , ans = 0; L = r; chkmin(r , mxl); int now = 1 , mxlen = 0; SAM.match(); while (l <= r) { int mid = (l + r) >> 1; if (check(mid)) { ans = mid; l = mid + 1; } else r = mid - 1; } printf("%d\n" , ans); } return 0; }