[NOI2018]你的名字

主要题意

求字符串$S$与$T$不同的子串总数

题解

先考虑$l = 1, r = |T|$的情况:

因为任意子串为字符串前缀的某些后缀,那么令$Lim[i]$表示$T[1...i]$在$S$上所能匹配的最大长度,$Posi[i]$表示$T$的后缀自动机上的点$i$的$endpos$集合中最靠前的位置,那么答案即为

$$Ans = \sum\limits_{i = 1}^{nodes} \max (0, Len[i] - min (Len[Father[i]], Lim[Posi[i]]))$$

接下来考虑$l, r$任意的情况:

原来能否在$S$的后缀自动机上往下走的判断依据只有当前节点是否存在$c$边,那么有了$l, r$的限制,就多需要判断向下的这个节点的$endpos$是否有存在于$[l + len, r]$($len$表示已匹配长度)区间的位置,$endpos$集合用动态开点线段树维护一下就好了

代码

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 
  5 using namespace std;
  6 
  7 typedef long long LL;
  8 
  9 const int MAXN = 2e06 + 10;
 10 const int MAXL = 20 + 5;
 11 
 12 const int INF = 0x7fffffff;
 13 
 14 int N, M, Q;
 15 char Orig[MAXN], Str[MAXN];
 16 
 17 int Root[MAXN]= {0};
 18 int Left[MAXN * MAXL]= {0}, Right[MAXN * MAXL]= {0};
 19 int tnodes = 0;
 20 void Modify (int& root, int left, int right, int pos) {
 21     if (! root)
 22         root = ++ tnodes;
 23     if (left == right)
 24         return ;
 25     int mid = (left + right) >> 1;
 26     pos <= mid ? Modify (Left[root], left, mid, pos) : Modify (Right[root], mid + 1, right, pos);
 27 }
 28 int Tree_Merge (int sl, int sr) {
 29     if (! sl || ! sr)
 30         return sl + sr;
 31     int p = ++ tnodes;
 32     Left[p] = Tree_Merge (Left[sl], Left[sr]);
 33     Right[p] = Tree_Merge (Right[sl], Right[sr]);
 34     return p;
 35 }
 36 bool Query (int root, int left, int right, int L, int R) {
 37     if (! root || left > right)
 38         return false;
 39     if (L <= left && right <= R)
 40         return true;
 41     int mid = (left + right) >> 1;
 42     if (L <= mid)
 43         if (Query (Left[root], left, mid, L, R))
 44             return true;
 45     if (R > mid)
 46         if (Query (Right[root], mid + 1, right, L, R))
 47             return true;
 48     return false;
 49 }
 50 
 51 struct SAM {
 52     int Tree[MAXN][30];
 53     int Father[MAXN];
 54     int Len[MAXN], Posi[MAXN];
 55     int last, nodes;
 56 
 57     void init () {
 58         for (int i = 1; i <= nodes; i ++) {
 59             memset (Tree[i], 0, sizeof (Tree[i]));
 60             Father[i] = 0, Posi[i] = 0;
 61         }
 62         last = nodes = 1;
 63     }
 64 
 65     void Append (int c, int pos, int type) {
 66         int fa = last, p = ++ nodes;
 67         last = p;
 68         Len[p] = Len[fa] + 1, Posi[p] = pos;
 69         while (fa && ! Tree[fa][c])
 70             Tree[fa][c] = p, fa = Father[fa];
 71         if (! fa)
 72             Father[p] = 1;
 73         else {
 74             int x = Tree[fa][c];
 75             if (Len[x] == Len[fa] + 1)
 76                 Father[p] = x;
 77             else {
 78                 int np = ++ nodes;
 79                 Len[np] = Len[fa] + 1, Father[np] = Father[x], Posi[np] = Posi[x];
 80                 Father[p] = Father[x] = np;
 81                 memcpy (Tree[np], Tree[x], sizeof (Tree[x]));
 82                 while (fa && Tree[fa][c] == x)
 83                     Tree[fa][c] = np, fa = Father[fa];
 84             }
 85         }
 86         if (type == 1)
 87             Modify (Root[p], 1, N, pos);
 88     }
 89     int Topo[MAXN];
 90     int Minp[MAXN];
 91     int buck[MAXN];
 92     /*void Merge_Minp () {
 93         for (int i = 1; i <= nodes; i ++)
 94             buck[i] = 0, Minp[i] = Posi[i];
 95         for (int i = 1; i <= nodes; i ++)
 96             buck[Len[i]] ++;
 97         for (int i = 1; i <= N; i ++)
 98             buck[i] += buck[i - 1];
 99         for (int i = nodes; i >= 1; i --)
100             Topo[buck[Len[i]] --] = i;
101         for (int i = nodes; i >= 1; i --)
102             Minp[Father[Topo[i]]] = min (Minp[Father[Topo[i]]], Minp[Topo[i]]);
103     }*/
104     void Merge_Tree () {
105         for (int i = 1; i <= nodes; i ++)
106             buck[i] = 0;
107         for (int i = 1; i <= nodes; i ++)
108             buck[Len[i]] ++;
109         for (int i = 1; i <= N; i ++)
110             buck[i] += buck[i - 1];
111         for (int i = nodes; i >= 1; i --)
112             Topo[buck[Len[i]] --] = i;
113         for (int i = nodes; i >= 1; i --)
114             Root[Father[Topo[i]]] = Tree_Merge (Root[Father[Topo[i]]], Root[Topo[i]]);
115     }
116 } ;
117 SAM S, T;
118 
119 int Lim[MAXN]= {0};
120 /*void Match (int l, int r) {
121     int p = 1, len = 0;
122     for (int i = 1; i <= M; i ++) {
123         int c = Str[i] - 'a';
124         while (p && ! S.Tree[p][c])
125             p = S.Father[p], len = S.Len[p];
126         while (p && ! Query (Root[S.Tree[p][c]], 1, N, l + len, r))
127             p = S.Father[p], len = S.Len[p];
128         S.Tree[p][c] ? (p = S.Tree[p][c], len ++) : (p = 1, len = 0);
129         Lim[i] = len;
130     }
131 }*/
132 void Work (int l, int r) {
133     int p = 1, len = 0;
134     for (int j = 1; j <= M; j ++) {
135         int c = Str[j] - 'a';
136         T.Append (c, j, 2);
137         while (true) {
138             if (S.Tree[p][c] && Query (Root[S.Tree[p][c]], 1, N, l + len, r)) {
139                 p = S.Tree[p][c], len ++;
140                 break;
141             }
142             if (! len)
143                 break;
144             len --;
145             if (len == S.Len[S.Father[p]])
146                 p = S.Father[p];
147         }
148         Lim[j] = len;
149     }
150     // T.Merge_Minp ();
151     // Match (l, r);
152 }
153 
154 LL Solve () {
155     LL ans = 0;
156     for (int i = 1; i <= T.nodes; i ++)
157         ans += max (0, T.Len[i] - max (T.Len[T.Father[i]], Lim[T.Posi[i]]));
158     return ans;
159 }
160 
161 int getnum () {
162     int num = 0;
163     char ch = getchar ();
164 
165     while (! isdigit (ch))
166         ch = getchar ();
167     while (isdigit (ch))
168         num = (num << 3) + (num << 1) + ch - '0', ch = getchar ();
169 
170     return num;
171 }
172 
173 int main () {
174     scanf ("%s", Orig + 1);
175     N = strlen (Orig + 1);
176     S.init ();
177     for (int i = 1; i <= N; i ++)
178         S.Append (Orig[i] - 'a', i, 1);
179     S.Merge_Tree ();
180     scanf ("%d", & Q);
181     for (int i = 1; i <= Q; i ++) {
182         scanf ("%s", Str + 1);
183         int l = getnum (), r = getnum ();
184         T.init ();
185         M = strlen (Str + 1);
186         Work (l, r);
187         LL ans = Solve ();
188         printf ("%lld\n", ans);
189     }
190 
191     return 0;
192 }
193 
194 /*
195 scbamgepe
196 3
197 smape 1 9
198 sbape 1 9
199 sgepe 1 9
200 */
201 
202 /*
203 scbamgepe
204 3
205 smape 2 7
206 sbape 3 8
207 sgepe 1 9
208 */
View Code

 

posted @ 2018-12-12 16:48  Colythme  阅读(195)  评论(0编辑  收藏  举报