Codeforces round #129 (Div. 1) E. Little elephant and strings
Question: give n strings and ask how many substrings each character string has (different positions, the same substrings are treated as different) appear among at least K of the N strings.
Solution: I learned a new skill of Sam. When creating Sam for these strings, instead of connecting them together and creating Sam, I first set up a trie tree for them, then, search for this trie tree. For the V node on the trie tree, when Sam is being created, it should be located behind his Father's Day on the trie tree, we use TTOM [u] to map the U nodes on the trie tree to the labels on Sam. The advantage of establishing Sam in this way is that I find any prefix of any string, and the node on the Sam that it matches must be the prefix of the string. Let's remember this first. How can we use it for a while. We require that all substrings of each string appear in at least K strings, so let's first look at all substrings, which substrings are contained in K strings and expressed on Sam are the nodes that have been matched by K strings. We use CNT [u] to indicate that the U node has been matched by several strings. Each time we take out a string, it can contribute 1 to the CNT [] value of some nodes. These nodes are the union of each prefix of the string in the node Sam to the root chain, this can be solved by using LCA. After calculating CNT [], check whether the CNT value of each node is greater than or equal to K. If yes, on the node u, the Val [u]-Val [Fa [u] substring is contained by more than K strings. Add [u] is used to represent this value. Finally, when calculating the answer to each string, it is the sum of each prefix of the string mapped to the node on Sam to the add on the Root chain.
Code:
#include<stdio.h>#include<algorithm>#include<string.h>#include<vector>#include<queue>#define ll __int64using namespace std ;const int maxn = 111111 ;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] , c[26][N] ; int cnt[N] ; int tot , last ; int ws[N] , wv[N] ; ll add[N] ; vector<int> vec[N] ; void init () ; void solve ( int , int ) ; inline int new_node ( int _val ) { val[++tot] = _val ; for ( int i = 0 ; i < 26 ; i ++ ) c[i][tot] = 0 ; cnt[tot] = fa[tot] = add[tot] = 0 ; vec[tot].clear () ; return tot ; } int ADD ( int k , int p ) { int i ; int np = new_node ( val[p] + 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 ( p && c[k][p] == q ) c[k][p] = 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 c[26][maxn] , tot ; int new_node () { for ( int i = 0 ; i < 26 ; i ++ ) c[i][tot] = 0 ; 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] - 'a' ; if ( !c[k][now] ) c[k][now] = new_node () ; now = c[k][now] ; } } }}queue<int> Q ;void SAM::init () { tot = 0 ; TtoM[0] = new_node ( 0 ) ; Q.push ( 0 ) ;#define v Trie::c[k][u] while ( !Q.empty () ) { int u = Q.front () ; Q.pop () ; for ( int k = 0 ; k < 26 ; k ++ ) if ( v ){ TtoM[v]=ADD(k,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 k ) { 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] - 'a' ; 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] ; if ( cnt[p] >= k ) add[p] = val[p] - val[fa[p]] ; } for ( int i = 1 ; i <= tot ; i ++ ) { int u = ws[i] ; for ( int j = head[u] ; j != -1 ; j = edge[j].next ) { int to = edge[j].to ; add[to] += add[u] ; } } for ( int i = 1 ; i <= n ; i ++ ) { int u = 0 ; ll ans = 0 ; for ( int j = l[i] ; j < l[i+1] ; j ++ ) { int k = s[j] - 'a' ; u = v ; ans += add[TtoM[u]] ; } printf ( "%I64d " , ans ) ; } puts ( "" ) ;}#undef vvoid init () { tot = 0 ; memset ( head , -1 , sizeof ( head ) ) ; lca.init () ; Trie::init () ;}int main () { f[0] = -1 ; for ( int i = 1 ; i < maxn << 2 ; i ++ ) f[i] = f[i>>1] + 1 ; int n , k ; while ( scanf ( "%d%d" , &n , &k ) != EOF ) { init () ; len = 0 ; for ( int i = 1 ; i <= n ; i ++ ) { scanf ( "%s" , 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 ;}/*3 2abcbcab3 2abcacab2 2abcbc1 1bc2 2abb4 4ababbabaaaabbbababaabababababa2 2ababbaba2 2ababab2 2abba*/