[BZOJ4566]-[Haoi2016]找相同字符-后缀自动姬上的统计问题

说在前面

免得me某一天愚蠢到了连这道题都不会做
于是还是记录下来


题目

BZOJ4566传送门

题面

给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同

输入输出格式

输入格式:
输入仅两行,每行包含一个字符串

输出格式:
输出一行一个整数,表示答案


解法

好像做法还挺多的…
me想的是统计每一个子串的贡献,于是对这两个串建一个 广义后缀自动姬(别被吓到了…只是建在一起而已)
然后每个节点维护cnt1和cnt2,第一个串主链的cnt1为1,第二个串主链cnt2为1,逆拓扑序累加上去
那么,对于自动姬上的每个节点,贡献就是 (lenp.len)cnt1cnt2 ( 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() ;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值