【border樹】【P2375】動物園

Description

給定一個字符串 \(S\),對每個前綴求長度不超過該前綴一半的公共前後綴個數。

共有 \(T\) 組數據,每組數據的輸出是 \(O(1)\) 的。

Limitations

\(1 \leq |S| \leq 10^6,~1 \leq T \leq 5\)

Solution

好水的NOI題

建出 \(border\) 樹,對於樹上每個節點,它的所有 \(border\) 與它的所有祖先一一對應。這是因爲每個節點的父親是它的最長 \(border\),數學歸納可以證明這個結論。

考慮對於這棵 \(border\) 樹上任何一個節點到根的路徑上節點的編號一定是單調減小的,這是因爲每個結點的最長 \(border\) 顯然小於自身。

因此,對於一個點 \(u\),如果 \(v\) 是一個合法的 \(border\),那麼 \(v\) 的祖先一定是合法的 \(border\)。所以合法的 \(border\) 一定是一條以根爲一個端點的鏈。

綜合上面兩條可以得出,如果 \(u\) 的合法 \(border\) 對應鏈的另一個端點爲 \(v\),那麼 \(u\) 的孩子的合法 \(border\) 的對應端點一定在 \(v\)\(u\) 的路徑上,因此這個端點位置是單調變深的。

所以只需要在 dfs 的時候維護根節點到該節點的鏈,並維護當前節點的答案位置即可。由於答案位置只會變大,因此答案位置的尋找是均攤 \(O(1)\) 的,暴力往下循環即可。

總時間複雜度 \(O(T |S|)\)

Code

#include <cstdio>
#include <cstring>
#include <vector>

const int maxn = 1000006;
const int MOD = 1000000007;

int T;
int n, top;
ll ans;
char MU[maxn];
int border[maxn], pos[maxn], stack[maxn];
std::vector<int>son[maxn];

void KMP();
void clear();
int ReadStr(char *p);
void dfs(const int u);

int main() {
  freopen("1.in", "r", stdin);
  qr(T);
  while (T--) {
    clear();
    n = ReadStr(MU + 1);
    KMP();
    for (int i = 1; i <= n; ++i) {
      son[border[i]].push_back(i);
    }
    dfs(0);
    qw(ans, '\n', true);
  }
  return 0;
}

int ReadStr(char *p) {
  auto beg = p;
  do *p = IPT::GetChar(); while ((*p > 'z') || (*p < 'a'));
  do *(++p) = IPT::GetChar(); while ((*p >= 'a') && (*p <= 'z'));
  *p = 0;
  return p - beg;
}

void KMP() {
  for (int i = 2, j = 0; i <= n; ++i) {
    while (j && (MU[i] != MU[j + 1])) j = border[j];
    if (MU[i] == MU[j + 1]) ++j;
    border[i] = j;
  }
}

void clear() {
  ans = 1; top = -1;
  for (auto &u : son) {
    u.clear();
  }
  memset(pos, 0, sizeof pos);
}

void dfs(const int u) {
  stack[++top] = u;
  for (auto v : son[u]) {
    int dv = v >> 1;
    int &_ans = pos[v];
    _ans = pos[u];
    while ((_ans < top) && (stack[_ans + 1] <= dv)) ++_ans;
    dfs(v);
  }
  if (pos[u]) {
    (ans += pos[u] * ans) %= MOD;
  }
  --top;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章