后面接的这段字符串长度显然不能超过最大匹配 $L[i]$,所以上限是 $i-L[i]$。
然后就是 $L[]$ 怎么求?
对所有子串建广义 SAM,然后跳 parent 树就行。
$i-L[i]$ 明显单调递增(因为前一个串包含后一个串),然后 dp 式中,$f[j]-j$ 和 $i$ 没关系,剩余部分 $i$ 也是单调递增。
所以整个式子满足决策单调性,可以单调队列搞。
## 代码
```cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2.2e6 + 5;
int n, m;
struct SAM
{
int tot, last, fa[N], len[N], t[N][2];
int q[N], f[N], mxlen[N];
inline SAM()
{
tot = last = 0;
fa[0] = -1;
}
inline void insert(char c)
{
c ^= 48;
int cur = ++tot;
len[cur] = len[last] + 1;
int p = last;
while (~p && !t[p][c])
t[p][c] = cur, p = fa[p];
last = cur;
if (!~p)
{
fa[cur] = 0;
return;
}
int x = t[p][c];
if (len[p] + 1 == len[x])
fa[cur] = x;
else
{
len[++tot] = len[p] + 1;
fa[tot] = fa[x];
t[tot][0] = t[x][0], t[tot][1] = t[x][1];
while (~p && t[p][c] == x)
t[p][c] = tot, p = fa[p];
fa[x] = fa[cur] = tot;
}
}
inline void match(string s)
{
int cur = 0, res = 0;
for (int i = 0; i < s.size(); i++)
{
int c = s[i] ^ 48;
if (t[cur][c])
res++, cur = t[cur][c];
else
{
while (~cur && !t[cur][c])
cur = fa[cur];
if (!~cur)
res = cur = 0;
else
res = len[cur] + 1, cur = t[cur][c];
}
mxlen[i + 1] = res;
}
}
inline bool check(int x, int len)
{
for (int i = 0; i < x; i++)
f[i] = 0;
for (int i = x, h = 1, t = 0; i <= len; i++)
{
while (h <= t && f[i - x] - (i - x) > f[q[t]] - q[t])
t--;
q[++t] = i - x;
while (h <= t && q[h] < i - mxlen[i])
h++;
if (h <= t)
f[i] = max(f[i - 1], f[q[h]] + i - q[h]);
}
return f[len] * 10 >= len * 9;
}
} sam;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
string s;
for (int i = 1; i <= m; i++)
{
cin >> s;
sam.last = 0;
for (char c : s)
sam.insert(c);
}
for (int i = 1; i <= n; i++)
{
cin >> s;
sam.match(s);
int l = 1, r = s.size(), len = s.size();
while (l <= r)
{
int mid = l + r >> 1;
if (sam.check(mid, len))
l = mid + 1;
else
r = mid - 1;
}
cout << l - 1 << '\n';
}
return 0;
}
```