说在前面
免得me某一天愚蠢到了连这道题都不会做
于是还是记录下来
题目
题面
给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同
输入输出格式
输入格式:
输入仅两行,每行包含一个字符串
输出格式:
输出一行一个整数,表示答案
解法
好像做法还挺多的…
me想的是统计每一个子串的贡献,于是对这两个串建一个 广义后缀自动姬(别被吓到了…只是建在一起而已)
然后每个节点维护cnt1和cnt2,第一个串主链的cnt1为1,第二个串主链cnt2为1,逆拓扑序累加上去
那么,对于自动姬上的每个节点,贡献就是
(len−p.len)∗cnt1∗cnt2
(
l
e
n
−
p
.
l
e
n
)
∗
c
n
t
1
∗
c
n
t
2
,即当前节点长度 * 从1选的方案数 * 从2选的方案数
下面是自带大常数的代码
#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
long long ans ;
char ss[200005] ;
int len , tp , head[800006] , id_c ;
struct Node{
int cnt[2] , len , id ;
map<int,Node*> ch ;
Node *par ;
} *root , *las , w[800005] , *tw = w ;
struct Path{
int pre , to ;
}p[800005] ;
void newNode( Node *&nd , int len ){
nd = ++ tw ; nd->len = len ;
nd->id = ++id_c ;
}
void Insert( int id , bool cate ){
Node *nd , *tmp = las ;
newNode( nd , las->len + 1 ) ;
nd->cnt[cate] = 1 ;
for( ; tmp && !tmp->ch.count( id ) ; tmp = tmp->par )
tmp->ch[id] = nd ;
if( !tmp ) nd->par = root ;
else{
Node *B = tmp->ch[id] ;
if( B->len == tmp->len + 1 ) nd->par = B ;
else{
Node *nB ; newNode( nB , tmp->len + 1 ) ;
nB->par = B->par ;
nd->par = B->par = nB ;
nB->ch = B->ch ;
while( tmp && tmp->ch[id] == B )
tmp->ch[id] = nB , tmp = tmp->par ;
}
} las = nd ;
}
void dfs( int &u ){
Node *nd = w + u ;
for( int i = head[u] , v ; i ; i = p[i].pre ){
v = p[i].to ;
dfs( v ) ;
nd->cnt[0] += w[v].cnt[0] ;
nd->cnt[1] += w[v].cnt[1] ;
}
if( u != 1 )
ans += 1LL * nd->cnt[0] * nd->cnt[1] * ( nd->len - nd->par->len ) ;
}
void solve(){
for( int i = 2 ; i <= id_c ; i ++ ){
p[++tp] = ( Path ){ head[ w[i].par->id ] , i } ;
head[ w[i].par->id ] = tp ;
} dfs( id_c = 1 ) ; printf( "%lld" , ans ) ;
}
int main(){
newNode( root , 0 ) ; root->par = NULL ;
las = root ;
scanf( "%s" , ss ) ; len = strlen( ss ) ;
for( int i = 0 ; i < len ; i ++ )
Insert( ss[i] - 'a' , 0 ) ;
las = root ;
scanf( "%s" , ss ) ; len = strlen( ss ) ;
for( int i = 0 ; i < len ; i ++ )
Insert( ss[i] - 'a' , 1 ) ;
solve() ;
}