HDU 3518 Boring counting - 后缀树

(其实这题我卡了一个月会乱说?)

基本就是参考之前的这篇文章。稍微多了点东西在里面。

另外,貌似我搜到的解题报告大都是用后缀数组的,不知道什么情况。


言归正传,建树的过程就不多说了,这里假设已经建立了目标字符串的后缀树。我们知道trie和后缀树是等价的,所以下文将不加区分的使用这两个概念。

如果想知道一个字符串中重复字串的个数,只需要把它的所有后缀插入到trie中(包括结束字符$)。我们知道,一个串的任何子串都可以表示成一个后缀的前缀。如果某个子串s出现了两次以上,就意味着原串中至少有后缀sa和sb,且a和b的长度不等。因此,如果trie中某个节点到所有其后叶节点的路径超过一条,那么从根节点到此节点这条路径所表示的串就是一个要求的字符串。并且由于这样的串是唯一出现的(trie路径的唯一性),所以统计一遍这样的节点的个数即可。

那么不重叠子串是怎样的呢?如果一个子串出现了数次,但都是重叠的(例如"ababa"中的"aba"),假设以这个子串为前缀的所有后缀为sa1,sa2,...,san,其中a1,a2,...an的长度递增,则a1和an的长度之差不会超过s的长度。

因为如果他们的差超过了s的长度,意味着我们可以在原串中找到两个后缀sa1和san,使得sa1是san的后缀;由于an的长度超过sa1,那么sa1是an的后缀,则an可以表示为?sa1,san就可以表示为s?sa1,即s不重叠的出现了两次。

这个命题的逆命题也是成立的。因此我们可以得到以下结论:

字符串的某个子串s不重复的出现两次以上,当且仅当在trie中沿着s的路径找到节点p,p到所有其子节点中的所有叶节点的距离的最大值和最小值之差不小于s的长度。

当然,后缀树由于压缩了所有只有一个子节点的trie树节点,因此不管是统计个数还是统计子节点的距离都比trie方便的多,何况trie还非常占用空间。

完整的代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <iostream>
#include <stack>
using namespace std;

char str[1024];

struct node{  
  int b,e,maxlen,minlen,prelen;
  node *link, *next[27], *parent;
  node* find_node(int db, int de, int& pos) const{  
    const node* p = this;  
    pos = p->b;  
    while (db < de){  
      int tb = p->b, te = p->e;  
      if (de - db <= te - tb){  
        pos = tb + de - db;  
        break;  
      }  
      db += te - tb;  
      p = p->next[str[db]-'a'];
    }  
    return (node*)p;  
  }  
  void clear(){
    memset(this,0,sizeof(*this));
    maxlen = -1; 
    minlen = 100000; 
  }  
}root,nodes[10000];  
int nodecount;
node* new_node(int b, int e, node* parent){
  node* ret = &nodes[nodecount++];
  ret->clear();
  ret->b = b;
  ret->e = e;
  ret->parent = parent;
  return ret;
}
  
void build_suffix_tree(){  
  int len = strlen(str)+1;  
  str[len-1] = 'a'+26;
  root.clear();
  root.next[str[0]-'a'] = new_node(0,len,&root);
  node* cur = &root;  
  
  for (int i = 1, m = 1, pos = 0; i < len; ++i){  
    node* last = 0;  
    for (int j = m; j <= i; ++j){  
      if (last){
        if (cur->link) {
          cur = cur->link;
          pos = cur->e;
        }
        else cur = root.find_node(j,i,pos);
      }
      if (pos < cur->e){  
        if (str[pos] == str[i]) {++pos; break;}  
        node* internal = new_node(cur->b,pos,cur->parent);
        node* leaf = new_node(i,len, internal);  
        cur->parent->next[str[internal->b]-'a'] = internal;
        cur->b = pos;  
        cur->parent = internal;
        internal->next[str[cur->b]-'a'] = cur;
        internal->next[str[leaf->b]-'a'] = leaf;
        cur = internal;
      }  
      else {  
        if (cur->next[str[i]-'a']){
          cur = cur->next[str[i]-'a'];
          pos = cur->b+1;
          break;
        }
        cur->next[str[i]-'a'] = new_node(i,len,cur);
      }  
      if (last) last->link = cur;  
      last = cur;  
      ++m;  
    }  
  }  
  str[len-1] = 0;
}

long long dfs(){
  long long ret = 0;
  stack<node*> st;
  st.push(&root);
  while (!st.empty()){
    node* p = st.top();
    bool isleaf = true;
    for (int i = 0, t = p->prelen+p->e-p->b; i < 27; ++i){
      node* q = p->next[i];
      if (!q) continue;
      isleaf = false;
      if (q->maxlen == -1) {q->prelen = t; st.push(q);}
      else {p->maxlen = max(p->maxlen,q->maxlen); p->minlen = min(p->minlen,q->minlen);}
    }  

    if (isleaf) {
      st.pop(); 
      p->maxlen = p->minlen = p->e - p->b;
      continue;
    }
    if (p->maxlen == -1) continue;
    p->maxlen += p->e - p->b;
    p->minlen += p->e - p->b;
    int t = (min(p->maxlen-p->minlen, p->prelen+p->e-p->b) - p->prelen);
    if (t > 0) ret += t;
    st.pop();
  }

  return ret;
}

int main(){
  while (scanf("%s", str) && str[0] != '#'){
    if (strlen(str) < 2){
      cout << 0 << endl;
    }
    else {
      nodecount = 0;
      build_suffix_tree();
      cout << dfs() << endl;
    }
  }
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值