bzoj4453
单调栈+set+后缀数组
一道奇妙的题
这道题如果对于每个询问$r$是固定的,那么就很简单了,可惜并不是
由于r会变化,那么对于两个子串$[i...r],[j...r]$,他们的大小关系随着r的变化也会变化,使得我们不能直接预处理答案
所以我们把询问离线,把每个询问按照r分类,通过考虑r的变化来完成询问
对于两个后缀$[i...r],[j...r],(i<j)$,设他们的$lcp=l$,那么当$l∈[j,j+l-1]$时,$[i...r]>[j...r]$,当$r=j+l$时,则变成$[i...r]<[j...r]$,因为$s[i+l]<s[j+l]$(假设)
我们称这样的两个后缀为$i$伴随$j$。
那么我们对答案维护一个set,里面存着一些后缀,保证后缀当前的大小单调递减,这样我们就可以每次快速询问答案。
但是由于$r$的变化每次大小关系会改变,也就是我们要把一些后缀删掉
那么我们再维护一个单调栈,这里存的是严格的后缀大小,这个单调栈是为了给每个新加入的后缀求出哪些后缀伴随他
那么如果当前的栈顶是大于新的后缀,$break$,否则标记$top$伴随$i$,并且记录当$i$,也就是$r=i+lcp$时删除栈顶
每次删除一个元素时,伴随他的元素也应该删除,所以每次$dfs$删除即可,并在$set$里删除
#include <cstdio> #include <cstring> #include <algorithm> #include <set> #include <vector> using namespace std; const int N = 1e5 + 5; int n, k, m, top; int rnk[N], tmp[N], sa[N], ans[N], vis[N], st[N], h[N][21], Log[N]; char s[N]; set<int> S; vector<pair<int, int> > v[N]; vector<int> del[N], g[N]; bool cmp(int i, int j) { if(rnk[i] != rnk[j]) { return rnk[i] < rnk[j]; } int ri = i + k <= n ? rnk[i + k] : -1; int rj = j + k <= n ? rnk[j + k] : -1; return ri < rj; } void dfs(int u) { vis[u] = 1; S.erase(u); for(int i = 0; i < g[u].size(); ++i) { if(!vis[g[u][i]]) { dfs(g[u][i]); } } } void SA() { for(int i = 1; i <= n; ++i) { sa[i] = i; rnk[i] = s[i]; } for(k = 1; k <= n; k <<= 1) { sort(sa + 1, sa + n + 1, cmp); tmp[sa[1]] = 1; for(int i = 2; i <= n; ++i) { tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i])); } for(int i = 1; i <= n; ++i) { rnk[i] = tmp[i]; } } for(int i = 1; i <= n; ++i) { rnk[sa[i]] = i; } int d = 0; for(int i = 1; i <= n; ++i) { if(rnk[i] <= 1) { continue; } int j = sa[rnk[i] - 1]; if(d) { --d; } for(; i + d <= n && j + d <= n; ++d) { if(s[i + d] != s[j + d]) { break; } } h[rnk[i] - 1][0] = d; } for(int j = 1; j <= 20; ++j) { for(int i = 1; i + (1 << j) - 1 <= n; ++i) { h[i][j] = min(h[i][j - 1], h[i + (1 << (j - 1))][j - 1]); } } } int rmq(int l, int r) { if(l == r) { return n - l + 1; } l = rnk[l]; r = rnk[r]; if(l > r) { swap(l, r); } --r; int x = Log[r - l + 1]; return min(h[l][x], h[r - (1 << x) + 1][x]); } int main() { scanf("%s%d", s + 1, &m); n = strlen(s + 1); for(int i = 2; i <= n; ++i) { Log[i] = Log[i >> 1] + 1; } SA(); for(int i = 1; i <= m; ++i) { int l, r; scanf("%d%d", &l, &r); v[r].push_back(make_pair(l, i)); } for(int i = 1; i <= n; ++i) { S.insert(i); while(top) { int lcp = rmq(st[top], i); if(s[st[top] + lcp] > s[i + lcp]) { break; } del[i + lcp].push_back(st[top]); g[i].push_back(st[top]); --top; } st[++top] = i; for(int j = 0; j < del[i].size(); ++j) { if(!vis[del[i][j]]) { dfs(del[i][j]); } } for(int j = 0; j < v[i].size(); ++j) { pair<int, int> x = v[i][j]; ans[x.second] = *S.lower_bound(x.first); } } for(int i = 1; i <= m; ++i) { printf("%d\n", ans[i]); } return 0; }