标签:sam
题意:给出n个串,再m个询问,每次询问一个串s,问给出的n个串中,子串包含s的有几个
解法:给这n个串建立trie,再将trie建成sam,然后我们要知道的是,对于每一个状态u所表示的子串,被几个串包含,这里跟http://blog.csdn.net/no__stop/article/details/38611209这题的处理方法类似,不再赘述。然后询问的串,就去sam上匹配,匹配到哪个节点,就将该节点被几个串包含输出即可。这题字符集有128,建立sam的时候,如果把无用的边也枚举一遍,复杂度过高O(128*2*len),所以用一个map把有用的边存起来就好了,复杂度为O(nlog(字符集))。
代码:
#include<stdio.h> #include<algorithm> #include<string.h> #include<vector> #include<queue> #include<map> using namespace std ; const int maxn = 101111 ; const int N = maxn << 1; struct Edge { int to , next ; } edge[N] ; int head[N] , tot , f[N<<1] ; void new_edge ( int a , int b ) { edge[tot].to = b ; edge[tot].next = head[a] ; head[a] = tot ++ ; } char s[maxn] , s1[maxn] ; int l[maxn] , len ; int TtoM[maxn<<1] ; struct LCA { int dp[22][N<<1] ; int to[N] , tim[N] ; int tot , n ; int MIN ( int a , int b ) { return tim[a] < tim[b] ? a : b ; } void init () { tot = 0 ; n = 0 ; } void dfs ( int u , int fa ) { tim[u] = ++ tot ; for ( int i = head[u] ; i != -1 ; i = edge[i].next ) { int v = edge[i].to ; if ( v == fa ) continue ; dfs ( v , u ) ; dp[0][++n] = u ; } dp[0][++n] = u ; to[u] = n ; } void rmq () { for ( int i = 1 ; i <= 20 ; i ++ ) { for ( int j = 1 ; j + (1<<i) - 1 <= n ; j ++ ) { dp[i][j] = MIN ( dp[i-1][j] , dp[i-1][j+(1<<i-1)] ) ; } } } int query ( int a , int b ) { a = to[a] , b = to[b] ; if ( a > b ) swap ( a , b ) ; int k = b - a + 1 ; return MIN ( dp[f[k]][a] , dp[f[k]][b-(1<<f[k])+1] ) ; } } lca ; namespace SAM { int fa[N] , val[N] ; map<int,int> c[N] ; int cnt[N] ; int tot , last ; int ws[N] , wv[N] ; void init () ; void solve ( int , int ) ; inline int new_node ( int _val ) { val[++tot] = _val ; c[tot].clear () ; cnt[tot] = fa[tot] = 0 ; return tot ; } int ADD ( int k , int p ) { int i ; int np = new_node ( val[p] + 1 ) ; while ( p && !c[p][k] ) c[p][k] = np , p = fa[p] ; if ( !p ) fa[np] = 1 ; else { int q = c[p][k] ; if ( val[q] == val[p] + 1 ) fa[np] = q ; else { int nq = new_node ( val[p] + 1 ) ; map<int,int> temp = c[q] ; for ( map<int,int>::iterator it = temp.begin() ; it != temp.end () ; ++ it ) c[nq][it->first] = it->second ; fa[nq] = fa[q] ; fa[q] = fa[np] = nq ; while ( p && c[p][k] == q ) c[p][k] = nq , p = fa[p] ; } } return np ; } void SORT () { for ( int i = 0 ; i < maxn ; i ++ ) wv[i] = 0 ; for ( int i = 1 ; i <= tot ; i ++ ) wv[val[i]] ++ ; for ( int i = 1 ; i < maxn ; i ++ ) wv[i] += wv[i-1] ; for ( int i = 1 ; i <= tot ; i ++ ) ws[wv[val[i]]--] = i ; } } namespace Trie { int tot ; map<int,int> c[maxn] ; int new_node () { c[tot].clear () ; return tot ++ ; } void init () { tot = 0 ; new_node () ; } void insert ( int n ) { for ( int i = 1 ; i <= n ; i ++ ) { int now = 0 ; for ( int j = l[i] ; j < l[i+1] ; j ++ ) { int k = s[j] ; if ( !c[now][k] ) c[now][k] = new_node () ; now = c[now][k] ; } } } } queue<int> Q ; void SAM::init () { tot = 0 ; TtoM[0] = new_node ( 0 ) ; Q.push ( 0 ) ; #define v Trie::c[u][k] while ( !Q.empty () ) { int u = Q.front () ; Q.pop () ; map<int,int> temp = Trie::c[u] ; for ( map<int,int>::iterator it = temp.begin() ; it != temp.end () ; ++ it ) { int V = it->second ; TtoM[V]=ADD(it->first,TtoM[u]) ; Q.push ( V ) ; } } } int cmp ( int a , int b ) { return lca.tim[a] < lca.tim[b] ; } int sta[maxn] ; void SAM::solve ( int n , int q ) { SORT () ; for ( int i = 2 ; i <= tot ; i ++ ) { new_edge ( fa[i] , i ) ; } lca.dfs ( 1 , 0 ) ; lca.rmq () ; for ( int i = 1 ; i <= n ; i ++ ) { int u = 0 ; int top = 0 ; for ( int j = l[i] ; j < l[i+1] ; j ++ ) { int k = s[j] ; u = v ; sta[++top] = TtoM[u]; cnt[TtoM[u]] ++ ; } sort ( sta + 1 , sta + top + 1 , cmp ) ; for ( int j = 2 ; j <= top ; j ++ ) { int w = lca.query ( sta[j-1] , sta[j] ) ; cnt[w] -- ; } } for ( int i = tot ; i >= 1 ; i -- ) { int p = ws[i] ; cnt[fa[p]] += cnt[p] ; } // for ( int i = 1 ; i <= tot ; i ++ ) // printf ( "cnt[%d] = %d\n" , i , cnt[i] ) ; for ( int i = 1 ; i <= q ; i ++ ) { gets ( s1 ) ; int len = strlen ( s1 ) ; int now = 1 ; for ( int j = 0 ; j < len ; j ++ ) { int k = s1[j] ; if ( !c[now].count(k) ) { now = 0 ; break ; } now = c[now][k] ; } // printf ( "now = %d\n" , now ) ; if ( !now ) puts ( "0" ) ; else printf ( "%d\n" , cnt[now] ) ; } } #undef v void init () { tot = 0 ; memset ( head , -1 , sizeof ( head ) ) ; lca.init () ; Trie::init () ; } int main () { // freopen ( "a.txt" , "r" , stdin ) ; // freopen ( "b.txt" , "w" , stdout ) ; f[0] = -1 ; for ( int i = 1 ; i < maxn << 2 ; i ++ ) f[i] = f[i>>1] + 1 ; int n , k ; scanf ( "%d%d" , &n , &k ) ; init () ; len = 0 ; getchar () ; int flag = 0 ; for ( int i = 1 ; i <= n ; i ++ ) { gets ( s1 ) ; int k = strlen ( s1 ) ; l[i] = len ; for ( int j = 0 ; j < k ; j ++ ) { s[len++] = s1[j] ; } } l[n+1] = len ; Trie::insert (n) ; SAM::init () ; SAM::solve ( n , k ) ; return 0 ; } /* 2 1000 aabab... ---babaa 2 1000 .a b- 3 3 hi,I'mChuYuXun..YouaresohandsomethatIfallinlovewithyou butIlovesevenk..you'dbettergoaway 55555555555 ChuYuXun you 55555555 */
Sevenk Love Oimaster(trie,MAP后缀自动机)
标签:sam
原文地址:http://blog.csdn.net/no__stop/article/details/40679749