1. 程式人生 > >LCS&LCS2 SAM SPOJ

LCS&LCS2 SAM SPOJ

LCS - Longest Common Substring

Description

給兩個長度\(\le 250000\)的小寫字母串, 求最長公共子串.

Solution

子串就是SAM了. 對其中一個建出SAM, 另一個在上面跑, 如果可以轉移, 就直接轉移, 長度加一, 否則跳父親到可以轉移, 自然是取\(max_{\alpha}\)了.

如果都不行就回到根.

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cctype>

using namespace std;

const int maxn = 700005;

int ans;
char s1[maxn], s2[maxn];

struct SuffixAutoMation
{
  int tr[maxn][26], fa[maxn], mx[maxn];
  int lst, cnt;
  SuffixAutoMation() {
    lst = cnt = 1;
  }
  void Insert(int c)  {
    int p = lst, np = ++cnt;
    lst = np; mx[np] = mx[p] + 1;
    for (; p && !tr[p][c]; p = fa[p]) tr[p][c] = np;
    if (!p) fa[np] = 1;
    else {
      int q = tr[p][c];
      if (mx[p] + 1 == mx[q]) fa[np] = q;
      else {
        int nq = ++cnt;
        mx[nq] = mx[p] + 1;
        for (int i = 0; i < 26; ++i) tr[nq][i] = tr[q][i];
        fa[nq] = fa[q]; fa[q] = fa[np] = nq;
        for (; tr[p][c] == q; p = fa[p]) tr[p][c] = nq;
      }
    }
    //siz[np] = 1;
  }
  void Build(int n) {
    for (int i = 1; i <= n; ++i)
      Insert(s1[i] - 'a');
  }
  void Solve(int n) {
    int pos = 1, len = 0;
    for (int i = 1; i <= n; ++i) {
      int ch = s2[i] - 'a';
      if (tr[pos][ch]) len++, pos = tr[pos][ch];
      else {
        for (; pos && !tr[pos][ch]; ) pos = fa[pos];
        if (pos) len = mx[pos] + 1, pos = tr[pos][ch];
        else pos = 1, len = 0;
      }
      ans = max(ans, len);
    }
  }
}SAM;

int main() {
  scanf("%s%s", s1 + 1, s2 + 1);
  int l1 = strlen(s1 + 1), l2 = strlen(s2 + 1);
  SAM.Build(l1);
  SAM.Solve(l2);
  printf("%d\n", ans);
  return 0;
}

LCS2 - Longest Common Substring

Description

還是小寫字串\((\le 100000)\), 不過增加到了\(10\)個.

Solution

建一個SAM, 其他的分別在上面跑, 求出\(ans_{\alpha}\{2, 3, 4, \ldots,n\}\), 和\(max_{\alpha}\)取個min.

注意實現細節.

由於跑了很多串, 可能出現匹配位置不同的情況, 注意parent樹節點的兒子的答案可以拿來取max. 用\(max\)的拓撲性就好了.

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cctype>

using namespace std;

const int maxn = 3e+5 + 5;

int n, ans;
int a[maxn], b[maxn];
char s[maxn];

struct SuffixAutoMation
{
  int tr[maxn][26], mx[maxn], sz[maxn], fa[maxn];
  int lst, cnt;
  int mxv[maxn], mnv[maxn];
  SuffixAutoMation() {
    lst = cnt = 1;
  }
  void Insert(int c) {
    int p = lst, np = ++cnt;
    lst = np; mx[np] = mx[p] + 1;
    for (; p && !tr[p][c]; p = fa[p]) tr[p][c] = np;
    if (!p) fa[np] = 1;
    else {
      int q = tr[p][c];
      if (mx[p] + 1 == mx[q]) fa[np] = q;
      else {
        int nq = ++cnt;
        mx[nq] = mx[p] + 1;
        for (int i = 0; i < 26; ++i) tr[nq][i] = tr[q][i];
        fa[nq] = fa[q]; fa[q] = fa[np] = nq;
        for (; tr[p][c] == q; p = fa[p]) tr[p][c] = nq;
      }
    }
    sz[np] = 1;
  }
  void RadixSort() {
    memset(b, 0, sizeof b);
    for (int i = 1; i <= cnt; ++i) b[mx[i]]++;
    for (int i = 1; i <= cnt; ++i) b[i] += b[i - 1];
    for (int i = 1; i <= cnt; ++i) a[b[mx[i]]--] = i;
  }
  void Build(int n) {
    for (int i = 1; i <= n; ++i) Insert(s[i] - 'a');
    memset(mnv, 0x3f, sizeof mnv);
  }
  void Work(int n) {
    int p = 1, len = 0;
    //memset(mxv, 0, sizeof mxv);
    for (int i = 1; i <= n; ++i)  {
      int ch = s[i] - 'a';
      if (tr[p][ch]) p = tr[p][ch], len++, mxv[p] = max(mxv[p], len);
      else {
        for (; p && !tr[p][ch]; ) p = fa[p];
        if (p) len = mx[p] + 1, p = tr[p][ch], mxv[p] = max(mxv[p], len);
        else p = 1, len = 0;
      }
    }
    for (int i = cnt; i; --i) {
      int x = a[i], f = fa[x];
      mxv[f] = max(mxv[f], min(mxv[x], mx[f]));
      mnv[x] = min(mnv[x], mxv[x]);
      mxv[x] = 0;
    }
  }
  void CheckAns() {
    for (int i = 1; i <= cnt; ++i) ans = max(ans, mnv[i]);
  }
}SAM;

int main() {
  scanf("%s", s + 1);
  //memset(mnv, 0x3f, sizeof mnv);
  int l = strlen(s + 1);
  SAM.Build(l);
  SAM.RadixSort();
  while (scanf("%s", s + 1) != EOF) {
    
    l = strlen(s + 1);
    //if (!l) break;
    ++n;
    SAM.Work(l);
  }
  SAM.CheckAns();
  printf("%d\n", ans);
  return 0;
}