[CTSC 2012][BZOJ 2806]Cheat

真是一道好题喵~

果然自动机什么的就是要和 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] 是单增的,事实如此)

移动的窗口,求窗口中最大的数……很眼熟啊!经验和直觉都告诉我们这就是单调队列……

大家理性理解一下就好,就好~

 

你可能感兴趣的:(2012)