您的位置:首页 > 其它

JZOJ3975. 【NOI2015模拟1.17】串

2016-03-23 19:07 381 查看

题目大意

给定两个整数n,k,以及n个字符串,求每个字符串中有多少个子串在至少k个字符串(包括其本身)中出现过。

l表示所有的串的总和的长度。

对于10%的数据, n,k≤100

对于30%的数据,n,k,l≤1000

对于100%的数据,n,k,l≤100000

题解

这题比较容易就能想到SA,而对于多个字符串的问题,SA通常是将多个串合并为一个,并在他们中间插入一些没有出现过的字符,将每个串分开。再看这题,对于一个串,他能贡献l个后缀(l为字符串长度),如果他的一个后缀的前缀至少出现在了k个串中,那么这个前缀能贡献其长度那么多个子串,对于一整个串来说,它的Ans就是所有后缀的 最长 出现k次 的前缀长度 之和。

所以我们现在就有了一个直观的想法,先合并所有的串,做一遍SA,然后,按照SA枚举一个区间的右端点,保证这个区间包含k个不同的字符串,这个区间的贡献就是min{Height[l+1~r]} ,然后用线段树维护。每次右端点变化之后都调整左端点,保证区间仍然有k个字符串。最后单点查询求出答案。

时间复杂度O(nlogn).

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std ;

#define N 200000 + 10
const int MX = 20 ;

char str
;
int T[4*N] ;
int SA
, Rank
, Height
;
int tax
, tp
;
int s
, f
[25] , tot
, ans
, from
;
int n , m = 255 , Q , k , ret ;

bool cmp( int x , int y , int w ) { return tp[x] == tp[y] && tp[x+w] == tp[y+w] ; }

void Rsort() {
memset( tax , 0 , sizeof(tax) ) ;
for (int i = 1 ; i <= n ; i ++ ) tax[Rank[tp[i]]] ++ ;
for (int i = 1 ; i <= m ; i ++ ) tax[i] += tax[i-1] ;
for (int i = n ; i >= 1 ; i -- ) SA[tax[Rank[tp[i]]]--] = tp[i] ;
}

void Suffix() {
for (int i = 1 ; i <= n ; i ++ ) Rank[i] = s[i] , tp[i] = i ;
Rsort() ;
for (int w = 1 , p = 1 ; p < n ; w += w , m = p ) {
p = 0 ;
for (int i = n - w + 1 ; i <= n ; i ++ ) tp[++p] = i ;
for (int i = 1 ; i <= n ; i ++ ) if ( SA[i] > w ) tp[++p] = SA[i] - w ;
Rsort() ;
swap( Rank , tp ) ;
Rank[SA[1]] = p = 1 ;
for (int i = 2 ; i <= n ; i ++ ) {
if ( !cmp( SA[i] , SA[i-1] , w ) ) p ++ ;
Rank[SA[i]] = p ;
}
}
for (int i = 1 , k = 0 ; i <= n ; i ++ ) {
if ( k ) k -- ;
int j = SA[Rank[i] - 1] ;
while ( s[i+k] == s[j+k] ) k ++ ;
Height[Rank[i]] = k ;
}
}

int find( int l , int r ) {
int j = log(r - l + 1) / log(2) ;
return min( f[l][j] , f[r-(1 << j)+1][j] ) ;
}

void RMQ() {
for (int i = 1 ; i <= n ; i ++ ) f[i][0] = Height[i] ;
for (int j = 1 ; j <= MX ; j ++ ) {
for (int i = 1 ; i <= n ; i ++ ) {
if ( i + (1 << (j - 1)) > n ) f[i][j] = f[i][j-1] ;
else f[i][j] = min( f[i+(1 << (j-1))][j-1] , f[i][j-1] ) ;
}
}
}

void Change( int v , int l , int r , int x , int y , int c ) {
if ( l == x && r == y ) {
T[v] = max( T[v] , c ) ;
return ;
}
int mid = (l + r) / 2 ;
if ( y <= mid ) Change( v + v , l , mid , x , y , c ) ;
else if ( x > mid ) Change( v + v + 1 , mid + 1 , r , x , y , c ) ;
else {
Change( v + v , l , mid , x , mid , c ) ;
Change( v + v + 1 , mid + 1 , r , mid + 1 , y , c ) ;
}
}

void Search( int v , int l , int r , int x ) {
if ( l == x && r == x ) {
ret = max( ret , T[v] ) ;
return ;
}
int mid = (l + r) / 2 ;
ret = max( ret , T[v] ) ;
if ( x <= mid ) Search( v + v , l , mid , x ) ;
else Search( v + v + 1 , mid + 1 , r , x ) ;
}

int main() {
scanf( "%d%d" , &Q , &k ) ;
int last = 255 ;
for (int i = 1 ; i <= Q ; i ++ ) {
scanf( "%s" , str + 1 ) ;
int l = strlen( str + 1 ) ;
if ( k == 1 ) ans[i] = l * (l + 1) / 2 ;
if ( last == 'a' - 1 ) last = 'z' ;
if ( n ) s[++n] = ++ last ;
for (int j = 1 ; j <= l ; j ++ ) s[++n] = str[j] , from
= i ;
}
if ( k == 1 ) {
for (int i = 1 ; i <= Q ; i ++ ) printf( "%d " , ans[i] ) ;
return 0 ;
}
m = max( m , last ) ;
Suffix() ;
RMQ() ;
int l = 1 ;
for (int r = 1 ; r <= n ; r ++ ) {
if ( from[SA[r]] ) tot[from[SA[r]]] ++ ;
if ( from[SA[r]] && tot[from[SA[r]]] == 1 ) tot[0] ++ ;
if ( tot[0] < k ) continue ;
while ( l < r && tot[0] > k ) {
tot[from[SA[l]]] -- ;
if ( !tot[from[SA[l]]] ) tot[0] -- ;
l ++ ;
}
while ( l < r && tot[0] == k ) {
if ( tot[0] == k ) Change( 1 , 1 , n , l , r , find( l + 1 , r ) ) ;
tot[from[SA[l]]] -- ;
if ( !tot[from[SA[l]]] ) { tot[from[SA[l]]] ++ ; break ; }
l ++ ;
}
if ( tot[0] == k ) Change( 1 , 1 , n , l , r , find( l + 1 , r ) ) ;
}
for (int i = 1 ; i <= n ; i ++ ) {
ret = 0 ;
Search( 1 , 1 , n , i ) ;
ans[from[SA[i]]] += ret ;
}
for (int i = 1 ; i <= Q ; i ++ ) printf( "%d " , ans[i] ) ;
return 0 ;
}


以上.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: