Gao the String II
题意:有字符串集合A,B。每个集合内最多有50个字符串,每个字符串长度不超过10,用A集合内的字符串link(link操作就是连接,比如已经link得到一个s,那么可以直接在s后面接上一个A里面的字符串,也可以将s的后缀跟要接上的串的前缀叠在一起连接)成一个长度不超过L的字符串s,问s与每一个B里面的字符串匹配,得到的匹配位置数之和最大是多少?ps:比赛的时候没注意到时不超过L啊,还以为一定要L,晕死。。。
解题思路:如果我们得到了所有的s,那么跟B去匹配就完事了。但是,很显然,s的可能情况太多。建立B的ac自动机(为什么想到自动机?不知道,反正就是想到了),然后定义dp状态,dp[i][j][k]表示s的长度为i,以A中的第j个字符串为结尾,匹配到自动机上的k节点时,最大的匹配点数是多少。要预处理很多东西,首先要预处理出,A集合里的字符串两两匹配的所有能link的位置,然后还要预处理从自动机的t1节点,接上一个A里面的每一个字符串的每一个后缀能增加多少个匹配点,接上之后,会匹配到那个节点上。这些弄好了,dp递推的复杂度才会合适。
代码:
解题思路:如果我们得到了所有的s,那么跟B去匹配就完事了。但是,很显然,s的可能情况太多。建立B的ac自动机(为什么想到自动机?不知道,反正就是想到了),然后定义dp状态,dp[i][j][k]表示s的长度为i,以A中的第j个字符串为结尾,匹配到自动机上的k节点时,最大的匹配点数是多少。要预处理很多东西,首先要预处理出,A集合里的字符串两两匹配的所有能link的位置,然后还要预处理从自动机的t1节点,接上一个A里面的每一个字符串的每一个后缀能增加多少个匹配点,接上之后,会匹配到那个节点上。这些弄好了,dp递推的复杂度才会合适。
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std ;
const int N = 51 ;
const int maxn = 501 ;
int dp[N][N][maxn] , inc[N][N][N] ;
vector<int> vec[N][N] ;
short add[12][N][maxn] , to[12][N][maxn] ;
char s1[N][N] , s2[N][N] ;
int n , m , l ;
int len[N] ;
struct AC_auto {
int c[26][maxn] , tot , cnt[maxn] , fail[maxn] ;
queue<int> Q ;
int new_node () {
int i ;
for ( i = 0 ; i < 26 ; i ++ ) c[i][tot] = 0 ;
fail[tot] = cnt[tot] = 0 ;
return tot ++ ;
}
void insert ( char *s ) {
int now = 0 ;
for ( ; *s ; s ++ ) {
int k = *s - 'a' ;
if ( !c[k][now] ) c[k][now] = new_node () ;
now = c[k][now] ;
}
cnt[now] ++ ;
}
void get_fail () {
int u = 0 , e , i , j ;
for ( i = 0 ; i < 26 ; i ++ )
if ( c[i][u] ) Q.push ( c[i][u] ) ;
while ( !Q.empty () ) {
u = Q.front () ;
Q.pop () ;
for ( i = 0 ; i < 26 ; i ++ ) {
if ( c[i][u] ) {
e = c[i][u] ;
j = fail[u] ;
fail[e] = c[i][j] ;
cnt[e] += cnt[fail[e]] ;
Q.push ( e ) ;
}
else c[i][u] = c[i][fail[u]] ;
}
}
}
void work () {
int i , j , k , t , now , s , v , x , p , q ;
for ( i = 0 ; i <= 11 ; i ++ )
for ( k = 1 ; k <= n ; k ++ )
for ( t = 0 ; t < tot ; t ++ )
add[i][k][t] = 0 ;
for ( i = 1 ; i <= n ; i ++ )
for ( k = 0 ; k < tot ; k ++ ) {
for ( v = 1 ; v <= len[i] ; v ++ ) {
now = k ;
for ( x = v ; x <= len[i] ; x ++ ) {
p = s1[i][x] - 'a' ;
now = c[p][now] ;
add[v][i][k] += cnt[now] ;
}
to[v][i][k] = now ;
}
}
for ( i = 0 ; i <= l ; i ++ )
for ( j = 1 ; j <= n ;j ++ )
for ( k = 0 ; k < tot ; k ++ )
dp[i][j][k] = -1111111 ;
for ( i = 1 ; i <= n ; i ++ ) {
if ( len[i] > l ) continue ;
now = 0 ;
x = 0 ;
for ( j = 1 ; j <= len[i] ; j ++ ) {
k = s1[i][j] - 'a' ;
now = c[k][now] ;
x += cnt[now] ;
}
dp[len[i]][i][now] = x ;
}
for ( i = 0 ; i < l ; i ++ )
for ( j = 1 ; j <= n ; j ++ )
for ( k = 0 ; k < tot ; k ++ ) {
if ( dp[i][j][k] == -1111111 ) continue ;
for ( t = 1 ; t <= n ; t ++ ) {
s = vec[j][t].size () ;
for ( x = 0 ; x < s ; x ++ ) {
v = vec[j][t][x] ;
p = i + inc[j][t][v] ;
if ( p > l ) continue ;
q = to[v][t][k] ;
dp[p][t][q] = max ( dp[p][t][q] , dp[i][j][k] + add[v][t][k] ) ;
}
}
}
int ans = 0 ;
for ( i = 1 ; i <= l ; i ++ )
for ( j = 1 ; j <= n ; j ++ )
for ( k = 0 ; k < tot ; k ++ )
ans = max ( ans , dp[i][j][k] ) ;
printf ( "%d\n" , ans ) ;
}
void init () {
tot = 0 ;
new_node () ;
}
} ac ;
void init () {
ac.init () ;
int i , j , k , t , l1 , l2 , flag ;
for ( i = 1 ; i <= m ; i ++ )
ac.insert ( s2[i] + 1 ) ;
ac.get_fail () ;
for ( i = 1 ; i <= n ; i ++ )
len[i] = strlen( s1[i] + 1 ) ;
for ( i = 1 ; i <= n ; i ++ )
for ( j = 1 ; j <= n ; j ++ ) {
vec[i][j].clear () ;
l1 = len[i] ;
l2 = len[j] ;
vec[i][j].push_back ( 1 ) ;
inc[i][j][1] = l2 ;
for ( k = l1 ; k >= 2 ; k -- ) {
if ( l1 - k + 1 >= l2 ) break ;
flag = 1 ;
for ( t = k ; t <= l1 ; t ++ )
if ( s1[i][t] != s1[j][t-k+1] ) {
flag = 0 ;
break ;
}
if ( flag ) {
vec[i][j].push_back ( l1 - k + 2 ) ;
inc[i][j][l1-k+2] = l2 - l1 + k - 1 ;
}
}
}
ac.work () ;
}
int main () {
int i , j , k ;
while ( scanf ( "%d%d%d" , &n , &m , &l ) != EOF ) {
for ( i = 1 ; i <= n ; i ++ )
scanf ( "%s" , s1[i] + 1 ) ;
for ( i = 1 ; i <= m ; i ++ )
scanf ( "%s" , s2[i] + 1 ) ;
init () ;
}
}