zoukankan      html  css  js  c++  java
  • hdu 4622 Reincarnation (后缀自动机)

    hdu 4622 Reincarnation (后缀自动机)

    题意:给出一个字符串,最长2000,q个询问,每次询问[l,r]区间内有多少个不同的字串。

    解题思路:之前写过一个后缀数组的解法http://blog.csdn.net/no__stop/article/details/9669325。这几天学了下后缀自动机,所以拿出来写了一下。具体是这么做的。

    首先我们要知道后缀自动机的一个性质:往自动机里添加一个字符,就会增加val[last] - val[fa[last]]个不曾出现过,且以当前字符结尾的后缀,即增加这么多个不曾出现过的字符。对于这个性质是怎么来的,我是从parent tree里面对于状态的定义,以及val所代表的意思来考虑的,解释起来也比较繁琐,想了解的可以给我留言,我们可以讨论下。下面我就默认大家都知道了这个性质了。那么我们可以这样做,先将询问排序,第一关键字是左端点,第二关键字是右端点。这么排的好处在哪里?对于query[i].l == query[i-1].l && query[i].r >= query[i-1].r的询问(这里query按排好序的顺序枚举),我们是不是在上一次询问时,已经处理出了[query[i-1].l , query[i-1].r]区间的ans了,所以当前的ans只要把前面的ans先加过来,然后把[query[i-1].r+1 , query[i].r]之间的字符一个个添加过来,每次添加时ans累加val[last] - val[fa[last]]就行了(这个就是前面所说的那个性质)。

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std ;
    
    const int maxn = 11111 ;
    
    struct sam {
        int val[maxn] , fa[maxn] , c[26][maxn] ;
        int tot , last ;
    
        inline int new_node ( int step ) {
            int i ;
            val[++tot] = step ;
            fa[tot] = 0 ;
            for ( i = 0 ; i < 26 ; i ++ ) c[i][tot] = 0 ;
            return tot ;
        }
    
        inline void extend ( int k ) {
            int i , p = last ;
            int np = new_node ( val[last] + 1 ) ;
            while ( p && !c[k][p] ) c[k][p] = np , p = fa[p] ;
            if ( !p ) fa[np] = 1 ;
            else {
                int q = c[k][p] ;
                if ( val[q] == val[p] + 1 ) fa[np] = q ;
                else {
                    int nq = new_node ( val[p] + 1 ) ;
                    for ( i = 0 ; i < 26 ; i ++ ) c[i][nq] = c[i][q] ;
                    fa[nq] = fa[q] ;//注意
                    fa[q] = fa[np] = nq ;
    				while ( c[k][p] == q && p ) c[k][p] = nq , p = fa[p] ;//不能漏
                }
            }
            last = np ;
        }
    
        int add ( int k ) {
            extend ( k ) ;
            return val[last] - val[fa[last]] ;
        }
    
        int build ( char *s , int len ) {
            int i , ret = 0 ;
            tot = 0 ;
            last = new_node ( 0 ) ;
            for ( i = 0 ; i < len ; i ++ ) ret += add ( s[i] - 'a' ) ;
            return ret ;
        }
    
    } suf ;
    
    struct query {
        int l , r , ans ;
    } q[maxn] ;
    int pos[maxn] ;
    
    bool cmp ( int i , int j ) {
        if ( q[i].l == q[j].l ) return q[i].r < q[j].r ;
        return q[i].l < q[j].l ;
    }
    
    char s[2222] , s1[2222] ;
    int main () {
        int cas , m , i , j ;
        scanf ( "%d" , &cas ) ;
        while ( cas -- ) {
            scanf ( "%s" , s ) ;
            scanf ( "%d" , &m ) ;
            for ( i = 1 ; i <= m ; i ++ ) {
                pos[i] = i ;
                scanf ( "%d%d" , &q[i].l , &q[i].r ) ;
                q[i].l -- , q[i].r -- ;
            }
            sort ( pos + 1 , pos + m + 1 , cmp ) ;
            int last ;
            for ( i = 1 ; i <= m ; i ++ ) {
                int cnt = 0 ;
                if ( i == 1 ) {
                    int len = 0 ;
                    for ( j = q[pos[i]].l ; j <= q[pos[i]].r ; j ++ )
                        s1[len++] = s[j] ;
                    s1[len] = 0 ;
                    cnt = suf.build ( s1 , len ) ;
                }
                else {
                    if ( q[pos[i]].l == q[pos[i-1]].l && q[pos[i]].r >= q[pos[i-1]].r ) {
                        for ( j = q[pos[i-1]].r + 1 ; j <= q[pos[i]].r ; j ++ )
                            cnt += suf.add ( s[j] - 'a' ) ;
                        cnt += last ;
                    }
                    else {
                        int len = 0 ;
                        for ( j = q[pos[i]].l ; j <= q[pos[i]].r ; j ++ )
                            s1[len++] = s[j] ;
                        s1[len] = 0 ;
                        cnt = suf.build ( s1 , len ) ;
                    }
                }
                last = q[pos[i]].ans = cnt ;
            }
            for ( i = 1 ; i <= m ; i ++ ) printf ( "%d
    " , q[i].ans ) ;
        }
    }
    


  • 相关阅读:
    基于Linux OpenJDK Debian的发行版的JAVA_HOME环境变量的正确目标是什么?
    redhat linux卸载默认的openjdk与安装sun的jdk
    更换介质:请把标有…… DVD 的盘片插入驱动器“/media/cdrom/”再按回车键“ 解决方法
    mysql 导出表结构和表数据 mysqldump用法
    转怎么让VI支持中文显示
    debian 更换sh的默认链接为bash
    基于percona-monitoring-plugins实现Zabbix的MySQL多端口自动发现监控
    elasticsearch中client.transport.sniff的使用方法和注意事项
    网络大数据分析 -- 使用 ElasticSearch + LogStash + Kibana 来可视化网络流量
    Parsing Netflow using Kibana via Logstash to ElasticSearch
  • 原文地址:https://www.cnblogs.com/riskyer/p/3312919.html
Copyright © 2011-2022 走看看