题目大意:
就是现在有m个01串代表搜集的文章, 现在要检查n篇作文, 对于每一篇作文找到最大的L值, 一篇文章的L值可以将这篇文章分割成多个部分, 其中长度>=L的且在m个01串中出现过的串称之为熟悉的串, 熟悉的串的长度加起来要达到文章总长度的90%就称这篇文章熟悉, 求最大的L使得这篇文章熟悉
大致思路:
首先将m篇文章连起来中间用另外的字符隔开, 然后将要检查的作文在这个自动机上惊醒遍历, 得到每个位置的字符向前匹配能得到的最大长度
不放设每个位置向前恩那个匹配到的最大长度为match[i]
对于前i个字符在长度L要求下能匹配到的最大长度记为dp[i], 那么不难发现首先dp[i]至少等于dp[i - 1], 然后可能取到dp[i] = max(dp[j] + i - j, i - match[i] <= j <= i - L)
那么我们对于dp[i] = max(dp[j] + i - j, i - match[i] <= j <= i - L)可以转换为dp[i] - i = max(dp[j] - j, i - match[i] <= j <= i - L), 因为不难发现dp[i]的意义可得dp[i] <= dp[i + 1]<= dp[i + 2]...
即dp满足不减性, 那么dp[i] - i在最好的情况下(即dp[i] = i全部匹配的情况下dp[i] - i = 0), dp[i] - i满足不增性, 也就是说dp[i] - i一定是单调的
那么当dp[i] - i要取到max(dp[j] - j, i - match[i] <= j <= i - L)中的最大值时, 由于dp[j] - j满足不增性, 如果有dp[i - L] - (i - L)最大时, 最优解一定是dp[i] = dp[j] - j + i ( j = i - L)
由于dp[i] - i <= dp[i + 1] - (i + 1)当dpi - L] - (i - L)不是最大时, 最优解取的是[i - match[i], i - L]中的第一个, 由于i- match[i]也是单调的, 所以只需要用一个队列维护一下就行了, 复杂度降为O(n), 至于如何确定L, 二分一下就行了
代码如下:
Result : Accepted Memory : 98112 KB Time : 988 ms
/************************************************************** Problem: 2806 User: Gatevin Language: C++ Result: Accepted Time:988 ms Memory:98112 kb ****************************************************************/ /* * Author: Gatevin * Created Time: 2015/4/27 17:43:08 * File Name: Rin_Tohsaka.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; #define foreach(e, x) for(__typeof(x.begin()) e = x.begin(); e != x.end(); ++e) #define SHOW_MEMORY(x) cout<<sizeof(x)/(1024*1024.)<<"MB"<<endl #define maxn 4444444 #define maxs 1111111 struct Suffix_Automation { struct State { State *par; State *go[3]; int val; void init(int _val = 0) { par = 0, val = _val; memset(go, 0, sizeof(go)); } }; State *last, *root, *cur; State nodePool[maxn]; State* newState(int val = 0) { cur->init(val); return cur++; } void init() { cur = nodePool; root = newState(); last = root; } void extend(int w) { State *p = last; State *np = newState(p->val + 1); while(p && p->go[w] == 0) { p->go[w] = np; p = p->par; } if(p == 0) { np->par = root; } else { State *q = p->go[w]; if(p->val + 1 == q->val) { np->par = q; } else { State *nq = newState(p->val + 1); memcpy(nq->go, q->go, sizeof(q->go)); nq->par = q->par; q->par = nq; np->par = nq; while(p && p->go[w] == q) { p->go[w] = nq; p = p->par; } } } last = np; } int match[maxs];//第i个字符向前能匹配到的最大字符数为match[i] int dp[maxs];//dp[i]表示前i个字符中能分割出的最多的匹配字符数 bool check(int L0, int len) { queue <int> Q; dp[0] = 0; //Q.push(0); for(int i = 1; i <= len; i++) { dp[i] = dp[i - 1]; if(i - L0 >= 0) { if(!Q.empty() && dp[i - L0] - (i - L0) > dp[Q.front()] - Q.front()) while(!Q.empty()) Q.pop();//最优解如果有一定是i - L0 Q.push(i - L0); } while(!Q.empty() && Q.front() < i - match[i])//j >= i - match[i] Q.pop(); if(!Q.empty()) dp[i] = max(dp[i], dp[Q.front()] + i - Q.front()); } if(dp[len]*10 >= 9*len) return true; else return false; } void solve(char *s) { int len = strlen(s); State *now = root; int cnt = 0; for(int i = 0; i < len; i++)//预处理出match[i]数组 { int w = s[i] - '0'; if(now->go[w]) { now = now->go[w]; cnt++; match[i + 1] = cnt; } else { while(now && now->go[w] == 0) { now = now->par; if(now) cnt = now->val; } if(now == 0) now = root, cnt = 0; else { now = now->go[w]; cnt++; match[i + 1] = cnt; } } } int L = 0, R = len, mid; int ans = 0; while(L <= R) { mid = (L + R) >> 1; if(check(mid, len)) { L = mid + 1; ans = mid; } else R = mid - 1; } printf("%d\n", ans); } }; Suffix_Automation sam; char s[maxs]; int n, m; int main() { scanf("%d %d", &n, &m); sam.init(); while(m--) { scanf("%s", s); int len = strlen(s); for(int i = 0; i < len; i++) sam.extend(s[i] - '0'); sam.extend(2); } while(n--) scanf("%s", s), sam.solve(s); return 0; }
Result : Wrong Answer Memory : -- Time : --
/* * Author: Gatevin * Created Time: 2015/4/27 17:43:08 * File Name: Rin_Tohsaka.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; #define foreach(e, x) for(__typeof(x.begin()) e = x.begin(); e != x.end(); ++e) #define SHOW_MEMORY(x) cout<<sizeof(x)/(1024*1024.)<<"MB"<<endl #define maxn 4444444 #define maxs 1111111 struct Suffix_Automation { struct State { State *par; State *go[3]; int val; void init(int _val = 0) { par = 0, val = _val; memset(go, 0, sizeof(go)); } }; State *last, *root, *cur; State nodePool[maxn]; State* newState(int val = 0) { cur->init(val); return cur++; } void init() { cur = nodePool; root = newState(); last = root; } void extend(int w) { State *p = last; State *np = newState(p->val + 1); while(p && p->go[w] == 0) { p->go[w] = np; p = p->par; } if(p == 0) { np->par = root; } else { State *q = p->go[w]; if(p->val + 1 == q->val) { np->par = q; } else { State *nq = newState(p->val + 1); memcpy(nq->go, q->go, sizeof(q->go)); nq->par = q->par; q->par = nq; np->par = nq; while(p && p->go[w] == q) { p->go[w] = nq; p = p->par; } } } last = np; } int match[maxs];//第i个字符向前能匹配到的最大字符数为match[i] int dp[maxs];//dp[i]表示前i个字符中能分割出的最多的匹配字符数 bool check(int L0, int len) { queue <int> Q; dp[0] = 0; Q.push(0); for(int i = 1; i <= len; i++) { dp[i] = dp[i - 1]; while(!Q.empty() && Q.front() < i - match[i])//筛除边界条件 Q.pop(); if(!Q.empty() && Q.front() <= i - L0) dp[i] = max(dp[i], dp[Q.front()] + i - Q.front()); //这里就是认为dp[i]是不减的, 弄成了贪心, 其实dp[i]的最优解不一定来自最前面一个位置 Q.push(i); } if(dp[len]*10 >= 9*len) return true; else return false; } void solve(char *s) { int len = strlen(s); State *now = root; int cnt = 0; for(int i = 0; i < len; i++)//预处理出match[i]数组 { int w = s[i] - '0'; if(now->go[w]) { now = now->go[w]; cnt++; match[i + 1] = cnt; } else { while(now && now->go[w] == 0) { now = now->par; if(now) cnt = now->val; } if(now == 0) now = root, cnt = 0; else { now = now->go[w]; cnt++; match[i + 1] = cnt; } } } int L = 1, R = len, mid; int ans = 0; while(L <= R) { mid = (L + R) >> 1; if(check(mid, len)) { L = mid + 1; ans = mid; } else R = mid - 1; } printf("%d\n", ans); } }; Suffix_Automation sam; char s[maxs]; int n, m; int main() { scanf("%d %d", &n, &m); sam.init(); while(m--) { scanf("%s", s); int len = strlen(s); for(int i = 0; i < len; i++) sam.extend(s[i] - '0'); sam.extend(2); } while(n--) scanf("%s", s), sam.solve(s); return 0; }