真是一道好题喵~
果然自动机什么的就是要和 dp 搞基才是王道有木有!
A:连 CTSC 都叫我们搞基,果然身为一个程序猿,加入 FFF 团是我此生最明智的选择。妹子什么闪边去,大家一起来搞基吧!
Q:教练你是什么时候产生了 dp 和自动机是同性的错觉~ 教练你又是什么时候产生了你还有个不入团的选择 ( 妹 子 )这样的错觉~
A:显而易见的,我们……
Q:教练不要转移话题啊!
A:显而易见的,我们先搞一个后缀自动机……
Q:等等,教练,多串的自动机要怎么写?
A:把几个串并在一起不就好了?
Q:不对啊,那查询的时候万一查到两个串相连的地方,不是悲剧了?
A:你傻啊!搞个分割符什么的不就解决了?!
Q:教练我的分割符是用 $ 还是用 # 呢?
(旁白:当然是 $ 了,$ 可是美金的意思,# 算什么,电话号码吗?)
A:。。。。。。
搞出了后缀自动机,就像我们经常做的一样,把每个节点能匹配的最长长度搞出来 (Q:我没文化你也不能这么逗我!)
把字串 S 在第 i 位能匹配到的最长长度 same[i] 搞出来
考虑二分答案L
就有了个显而易见的 dp 转移方程:
f[i]=max{f[j]-j+i | i-same[i]<=j<=i-L}
然后题目就被妥妥的解决了
Q:不对啊教练,这 dp 是 O(n2) 的!
f[i]=max{f[j]-j+i | i-same[i]<=j<=i-L} ↔ f[i]-i=max{f[j]-j | i-same[i]<=j<=i-L}
A:这显然是单调的,优化优化就 O(n) 了
Q:教练求证明……
A:这个显然嘛,显然……要不然这题还怎么做额?
Q:教练……
A:烦死了,你打个表不就行了!
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 const int sizeOfType=3; 5 const int sizeOfString=1200021; 6 const int sizeOfMemory=5200025; 7 8 inline void getstring(char * s) 9 { 10 register char ch; 11 register int top=0; 12 do ch=getchar(); while (ch<‘0‘ || ch>‘1‘); 13 do s[top++]=ch, ch=getchar(); while (ch>=‘0‘ && ch<=‘1‘); 14 s[top]=‘\0‘; 15 } 16 17 struct deque 18 { 19 int l, r; 20 int q[sizeOfString]; 21 inline deque():l(0), r(-1) {} 22 inline void clear() {l=0, r=-1;} 23 inline bool empty() {return l>r;} 24 inline int front() {return q[l];} 25 inline int back() {return q[r];} 26 inline void push(int x) {q[++r]=x;} 27 inline void pop_front() {++l;} 28 inline void pop_back() {r--;} 29 }; 30 31 int N, M; 32 int len; 33 int same[sizeOfString]; 34 char str[sizeOfString]; 35 deque q; 36 int f[sizeOfString], g[sizeOfString]; 37 inline int max(int x, int y) {return x>y?x:y;} 38 inline void dp(int); 39 40 namespace suffixAutomaton 41 { 42 struct node 43 { 44 int step; 45 node * fail; 46 node * ch[sizeOfType]; 47 }; 48 node memory[sizeOfMemory], * port=memory; 49 node * dfa, * last; 50 inline node * newnode(node * t=NULL) 51 { 52 node * newt=port++; 53 newt->step=0; 54 if (t) newt->fail=t->fail, t->fail=newt, memcpy(newt->ch, t->ch, sizeof t->ch); 55 else newt->fail=NULL, memset(newt->ch, 0, sizeof newt->ch); 56 return newt; 57 } 58 inline void clear() {port=memory; dfa=newnode(); dfa->fail=dfa; last=dfa;} 59 60 inline int ord(char ch) {return ch>=‘0‘?ch-‘0‘:2;} 61 inline void insert(int w) 62 { 63 node * p=last, * newp=newnode(); 64 newp->step=p->step+1; 65 66 for ( ;p->ch[w]==NULL;p=p->fail) p->ch[w]=newp; 67 if (p->ch[w]==newp) 68 newp->fail=dfa; 69 else 70 { 71 node * q=p->ch[w]; 72 if (q->step==p->step+1) 73 newp->fail=q; 74 else 75 { 76 node * newq=newnode(q); 77 newq->step=p->step+1; 78 newp->fail=newq; 79 for ( ;p->ch[w]==q;p=p->fail) p->ch[w]=newq; 80 } 81 } 82 83 last=newp; 84 } 85 inline void buildDfa() 86 { 87 static char s[sizeOfString]; 88 int len; 89 90 clear(); 91 for (int i=0;i<M;i++) 92 { 93 getstring(s); len=strlen(s); 94 for (int j=0;j<len;j++) 95 insert(ord(s[j])); 96 insert(ord(‘$‘)); 97 } 98 } 99 inline void search(char * s) 100 { 101 int tot=0; 102 node * t=dfa; 103 104 for (int i=1;i<=len;i++) 105 { 106 int w=ord(s[i-1]); 107 if (t->ch[w]) 108 { 109 t=t->ch[w]; 110 ++tot; 111 } 112 else 113 { 114 node * j; 115 for (j=t->fail;j!=dfa && !j->ch[w];j=j->fail); 116 if (j->ch[w]) 117 { 118 t=j->ch[w]; 119 tot=j->step+1; 120 } 121 else 122 { 123 t=dfa; 124 tot=0; 125 } 126 } 127 same[i]=tot; 128 } 129 } 130 } 131 132 int main() 133 { 134 scanf("%d %d", &N, &M); 135 suffixAutomaton::buildDfa(); 136 for (int i=1;i<=N;i++) 137 { 138 getstring(str); 139 len=strlen(str); 140 suffixAutomaton::search(str); 141 142 int L=0, R=len+1; 143 while (L+1<R) 144 { 145 int M=(L+R)>>1; 146 dp(M); 147 if (10*f[len]>=9*len) L=M; 148 else R=M; 149 } 150 151 printf("%d\n", L); 152 } 153 154 return 0; 155 } 156 inline void dp(int L) 157 { 158 q.clear(); 159 160 for (int i=1;i<=len;i++) 161 { 162 int j=i-L; 163 if (j>=0) 164 { 165 for ( ;!q.empty() && g[j]>g[q.back()];q.pop_back()); 166 q.push(j); 167 } 168 f[i]=f[i-1]; 169 for ( ;!q.empty() && q.front()<i-same[i];q.pop_front()); 170 if (!q.empty()) 171 f[i]=max(f[i], g[q.front()]+i); 172 g[i]=f[i]-i; 173 } 174 }
后记:
话说我根本不知道如何证明单调性,但是大家注意到这是类似于一个一直在往右移动的窗口(不要问我为什么 i-same[i] 是单增的,事实如此)
移动的窗口,求窗口中最大的数……很眼熟啊!经验和直觉都告诉我们这就是单调队列……
大家理性理解一下就好,就好~
[CTSC 2012][BZOJ 2806]Cheat,布布扣,bubuko.com
原文:http://www.cnblogs.com/dyllalala/p/3915579.html