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 ; }
以上.
相关文章推荐
- 阿里二面跪
- 本地分支与远程分支建立关联
- 51nod动态规划入门--最长公共子序列
- css中hover伪类的bug
- 通用的用户登录过滤器(SessionFilter)
- 贪心算法 水题 求中间数(problem Q)
- Qt5以上QMYSQL driver not loaded问题
- prim 与 dijkstra 的区别
- C - Line——(扩展欧几里得算法)
- 三菱机器人中断简介
- iOS-3D touch相关开发
- 第二周项目4:穷举法解决组合问题(4)
- DP泛做
- 最大公约数与最小公倍数————多种方法
- junit的使用
- 第三周内容
- C++获取数组的长度(用模板可以实现)
- uvalive 7480 Association for Control Over Minds
- JDBC
- Python~第三方模块