后缀自动机合集 I

简介


有关后缀自动机的理论和证明各路大神的博客多如牛毛,就不细说。

贴图太占载入速度了,还是去看原版的解析吧  http://hi.baidu.com/myidea/item/142c5cd45901a51820e25039

对于每个State点,father指向与该节点可接收相同后缀的节点,其表示的字串通常是当前节点表示字串的后面部分,

因此State->step - Sate->father->step 代表从任一后缀串起始走到该状态的路径数


hdu 4622 Reincarnation

对每个状态点减去父状态的步骤数的和就是字符串所有字串的数量

#include <stdio.h>
#include <vector>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAXN = 2010;
int ans[MAXN][MAXN];
char s[MAXN];
struct node
{
    node*nxt[26], *fa;
    int step;
    void clear()
    {
        fa = 0, step = 0;
        memset ( nxt, 0, sizeof nxt );
    }
    int calc()
    {
        if ( !fa ) { return 0; }
        return step - fa->step;
    }
} *root, *tail, nodePool[MAXN * 2], *cur;
void init()
{
    cur = nodePool;
    root = tail = cur++;
    root->clear();
}
int tot;
void Insert ( int w )
{
    node *p = tail, *np = cur++;
    np->clear();
    np->step = p->step + 1;
    for ( ; p && !p->nxt[w]; p = p->fa ) { p->nxt[w] = np; }
    if ( !p )
    {
        np->fa = root;
        tot += np->calc();
    }
    else
    {
        if ( p->nxt[w]->step == p->step + 1 )
        {
            np->fa = p->nxt[w];
            tot += np->calc();
        }
        else
        {
            node *q = p->nxt[w], *r = cur++;
            *r = *q;
            tot -= q->calc();
            r->step = p->step + 1;
            q->fa = np->fa = r;
            tot += q->calc() + r->calc() + np->calc();
            for ( ; p && p->nxt[w] == q; p = p->fa ) { p->nxt[w] = r; }
        }
    }
    tail = np;
}


int main()
{
#ifdef  __GNUC__
    freopen ( "in.txt", "r", stdin );
#endif // __GNUC__
    int t;
    scanf ( "%d", &t );
    while ( t-- )
    {
        scanf ( "%s", s );
        int n = strlen ( s );
        for ( int i = 0; i < n; ++i )
        {
            init();
            tot = 0;
            for ( int j = i; j < n; ++j )
            {
                Insert ( s[j] - 'a' );
                ans[i][j] = tot;
            }
        }
        
        int q;
        scanf ( "%d", &q );
        while ( q-- )
        {
            int l, r;
            scanf ( "%d%d", &l, &r );
            l--, r--;
            printf ( "%d\n", ans[l][r] );
        }
    }
    return 0;
}

hdu 1403 Longest Common Substring

每个step姑且认为是转移到该状态时可接收的最大字符串

最长公共子串
#include <stdio.h>
#include <vector>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
char s[MAXN];
struct node
{
    node*nxt[26], *fa;
    int step;
    void clear()
    {
        fa = 0, step = 0;
        memset ( nxt, 0, sizeof nxt );
    }
} *root, *tail, nodePool[MAXN << 1], *cur;
void init()
{
    cur = nodePool;
    root = tail = cur++;
    root->clear();
}
void Insert ( int w )
{
    node *p = tail, *np = cur++;
    np->clear();
    np->step = p->step + 1;
    for ( ; p && !p->nxt[w]; p = p->fa ) { p->nxt[w] = np; }
    if ( !p )
    {
        np->fa = root;
    }
    else
    {
        if ( p->nxt[w]->step == p->step + 1 )
        {
            np->fa = p->nxt[w];
        }
        else
        {
            node *q = p->nxt[w], *r = cur++;
            *r = *q;
            r->step = p->step + 1;
            q->fa = np->fa = r;
            for ( ; p && p->nxt[w] == q; p = p->fa ) { p->nxt[w] = r; }
        }
    }
    tail = np;
}
int solve()
{
	int l = strlen(s), w, res = 0, sum= 0;
	node *p = root;
	for (int i = 0; i< l; ++i)
	{
		w = s[i]-'a';
		if ( p->nxt[w])
		{
			++sum;
			p = p->nxt[w];
		}
		else
		{
			while ( p && p->nxt[w] == NULL) p=p->fa;
			if ( p== NULL)
			{
				sum = 0;
				p = root;
			}
			else
			{
				sum = p->step + 1;
				p = p->nxt[w];
			}
		}
		res = max(sum, res);
	}
	return res;
}
int main()
{
#ifdef  __GNUC__
    freopen ( "in.txt", "r", stdin );
#endif // __GNUC__
    while ( scanf("%s", s) != EOF)
    {
        init();
        int l = strlen(s);
        for (int i = 0; i< l; ++i)
		{
			Insert(s[i]-'a'); 
		}
		scanf("%s", s);
		printf("%d\n", solve());
    }
    return 0;
}


hdu 4416 Good Article Good sentence

菜鸟对SAM的理解还是太弱了,借鉴了别人的代码才搞懂

本题用SAM来做,核心是记录下到达每个State时能得到Bi字串的最大长度。

再此还要解释下,引入father指针的目的是最大化状态的重用性,每个状态的父节点表示的串 == 当前状态所表示的字符串最后几位

因此,父节点的deep要更新为自身及子节点deep的最大值

每个节点deep - step 就表示为除去不符合要求串数量的后的值

#include <stdio.h>
#include <vector>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 100010;
char s[MAXN];
struct node
{
    node*nxt[26], *fa;
    int step;
    int deep;
    void clear()
    {
        fa = 0, step = 0; deep = 0;
        memset ( nxt, 0, sizeof nxt );
    }
} *root, *tail, nodePool[MAXN << 1], *cur;
void init()
{
    cur = nodePool;
    root = tail = cur++;
    root->clear();
}
void Insert ( int w )
{
    node *p = tail, *np = cur++;
    np->clear();
    np->step = p->step + 1;
    for ( ; p && !p->nxt[w]; p = p->fa ) { p->nxt[w] = np; }
    if ( !p )
    {
        np->fa = root;
    }
    else
    {
        if ( p->nxt[w]->step == p->step + 1 )
        {
            np->fa = p->nxt[w];
        }
        else
        {
            node *q = p->nxt[w], *r = cur++;
            *r = *q;
            r->step = p->step + 1;
            q->fa = np->fa = r;
            for ( ; p && p->nxt[w] == q; p = p->fa ) { p->nxt[w] = r; }
        }
    }
    tail = np;
}
void update()
{
    int l = strlen ( s ), w, sum = 0;
    node *p = root;
    for ( int i = 0; i < l; ++i )
    {
        w = s[i] - 'a';
        if ( p->nxt[w] )
        {
            ++sum;
            p = p->nxt[w];
        }
        else
        {
            while ( p && p->nxt[w] == NULL ) { p = p->fa; }
            if ( p == NULL )
            {
                sum = 0;
                p = root;
            }
            else
            {
                sum = p->step + 1;
                p = p->nxt[w];
            }
        }
        p->deep = max ( p->deep, sum );
    }
}
int p[MAXN << 1]; node *op[MAXN << 1];
int main()
{
#ifdef  __GNUC__
    freopen ( "in.txt", "r", stdin );
#endif // __GNUC__
    int t, cs = 0;
    int n;
    scanf ( "%d", &t );
    
    while ( t-- )
    {
        printf ( "Case %d: ", ++cs );
        scanf ( "%d", &n );
        scanf ( "%s", s );
        init();
        int l = strlen ( s );
        for ( int i = 0; i < l; ++i ) { Insert ( s[i] - 'a' ); }
        for ( int i = 0; i < n; ++i )
        {
            scanf ( "%s", s );
            update();
        }
        LL res = 0;
        memset ( p, 0, sizeof p );
        for ( int i = 0; i < cur - root; ++i ) { p[nodePool[i].step]++; }
        for ( int i = 1; i <= tail->step; ++i ) { p[i] += p[i - 1]; }
        for ( int i = 0; i < cur - root; ++i ) { op[--p[nodePool[i].step]] = nodePool + i; }
        for ( int i = cur - root - 1; i > 0; --i )
        {
            node *u = op[i];
            if ( u->deep > 0 )	
            {
                u->fa->deep = max ( u->fa->deep, u->deep );
                if ( u->deep < u->step )
                {
                    res += u->step - u->deep ;
                }
            }
            else
            {
                res += u->step - u->fa->step;
            }
        }
#ifdef __GNUC__
        printf ( "%lld\n", res );
#else
        printf ( "%I64d\n", res );
#endif
    }
    return 0;
}


hdu 4270 Dynamic Lover

关键问题是后缀自动机状态删除,可行的办法就是设置标记表示该状态是否被删除,且同一状态的拷贝点也指向相同的标记

#include <stdio.h>
#include <vector>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAXN = 200020;
char s[MAXN ];
int isDel[MAXN], delCnt;
struct node
{
    node*nxt[26], *fa;
    int step;
    int *isdel;
    int pos;
    node *flg;
    void clear()
    {
        fa = 0, step = 0; isdel = 0; flg = 0;
        memset ( nxt, 0, sizeof nxt );
    }
} *root, *tail, nodePool[MAXN << 1], *cur, *mseq[MAXN], *cnt;
void init()
{
    memset ( isDel, 0, sizeof isDel );    
    cur = nodePool;
    root = tail = cur++;
    root->clear();
    mseq[0] = root;
    root->isdel = isDel;
    delCnt = 1;
}
int judge ( node *x )
{
    return x == NULL || *x->isdel;
}
void Insert ( int w )
{
    node *p = tail, *np = cur++;
    np->clear();
    np->step = p->step + 1;
    np->pos = np->step;
    mseq[np->pos] = np;
    np->isdel = isDel + delCnt++;
    for ( ; p && judge ( p->nxt[w] ); p = p->fa ) { p->nxt[w] = np; }
    if ( !p )
    {
        np->fa = root;
    }
    else
    {
        if ( p->nxt[w]->step == p->step + 1 )
        {
            np->fa = p->nxt[w];
        }
        else
        {
            node *q = p->nxt[w], *r = cur++;
            *r = *q;
            r->step = p->step + 1;
            q->fa = np->fa = r;
            for ( ; p && p->nxt[w] == q; p = p->fa ) { p->nxt[w] = r; }
        }
    }
    tail = np;
}
int flag, tl;
void dfs ( node *k, int l )
{
    if ( flag ) { return; }
    if ( l == tl ) {flag = k->pos - l + 1; return; }
    if ( k->flg == cnt )
    {
        flag = cnt->pos - l + 1;
        return;
    }
    for ( int i = 0; !flag && i < 26; ++i )
    {
        if ( k->nxt[i] && !*k->nxt[i]->isdel )
        {
            dfs ( k->nxt[i], l + 1 );
        }
    }

}

int main()
{
#ifdef  __GNUC__
    freopen ( "in.txt", "r", stdin );
#endif // __GNUC__
    int n, c, k;
    char *p;
    while ( scanf ( "%s", s ) != EOF )
    {
        init();
        for ( p = s; *p; ++p ) { Insert ( *p - 'a' ); }
        scanf ( "%d", &n );
        while ( n-- )
        {
            scanf ( "%d", &c );
            if ( c == 1 )
            {
                scanf ( "%s", s );
                char *p = s;
                for ( ; *p; ++p ) { Insert ( *p - 'a' ); }
            }
            else if ( c == 2 )
            {
                cnt = tail;
                for ( node *as = tail; as != root ; as = as->fa )
                {
                    as->flg = cnt;
                }
                scanf ( "%d", &k );
                flag = 0; tl = k;
                dfs ( root, 0 );
                printf ( "%d\n", flag );
            }
            else
            {
                scanf ( "%d", &k );
                while ( k-- )
                {
                    *tail->isdel = 1;
                    tail = mseq[tail->step - 1];
                }
            }
        }
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值