bzoj4566 [Haoi2016]找相同字符

http://www.elijahqi.win/archives/3036
Description

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

两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母

Output

输出一个整数表示答案

Sample Input

aabb
bbaa
Sample Output

10
HINT

Source

感谢sbw巨佬指点orz

首先将A串建出SAM然后将B串在A串上跑 先求出A串每个状态的right集合的大小 然后如果匹配到这个点就用(l-len[fa[x]])*size[x] 表示B的每一个前缀在A中的匹配方案数 那么光这样是不行的我们少计算了这样一个前缀的所有后缀在A中的匹配方案数所以我设e表示每个字串在A中状态出现的次数即计算时候的系数 sub表示这个节点的这种状态会被多少个B用上 然后又因为这个节点表示len[fa[p]]-len[p]这么多子串且出现次数为size[p]

sub[x]size[x](len[x]-len[fa[x]])

#include<cstdio>
#include<cctype>
#include<cstring>
#define ll long long
#include<algorithm>
using namespace std;
const int N=200020; 
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
int cnt=1,last=1,root=1,ch[N<<1][26],len[N<<1],fa[N<<1],size[N<<1];
int C[N],rk[N<<1],e[N<<1],sub[N<<1];ll ans;
inline void insert1(int x){static int p,np,q,nq;
    np=++cnt,p=last;len[np]=len[p]+1;
    for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
    if (!p) fa[np]=root;else{
        q=ch[p][x];if (len[p]+1==len[q]) fa[np]=q;else{
            nq=++cnt;len[nq]=len[p]+1;fa[nq]=fa[q];memcpy(ch[nq],ch[q],sizeof(ch[nq]));
            fa[q]=fa[np]=nq;for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }size[np]=1;last=np;
}
int main(){
//  freopen("bzoj4566.in","r",stdin);
    char c=gc();while(c<'a'||c>'z') c=gc();int le=0;
    while(c>='a'&&c<='z') insert1(c-'a'),c=gc(),++le;
    for (int i=1;i<=cnt;++i) ++C[len[i]];
    for (int i=1;i<=le;++i) C[i]+=C[i-1];
    for (int i=1;i<=cnt;++i) rk[C[len[i]]--]=i;
    for (int i=cnt;i;--i){
        static int x;x=rk[i];
        size[fa[x]]+=size[x];
    }c=gc();while(c<'a'||c>'z') c=gc();int p=1,l=0;
    while(c>='a'&&c<='z'){static int x;x=c-'a';
        if (ch[p][x]) {p=ch[p][x];++l;}else{
            for (;p&&!ch[p][x];p=fa[p]);
            if (!p) {p=1;l=0;}else l=len[p]+1,p=ch[p][x];
        }c=gc();ans+=(ll)(l-len[fa[p]])*size[p];++e[p];
    }
    for (int i=cnt;i>=2;--i){
        static int x;x=rk[i];
        sub[fa[x]]+=sub[x]+e[x];
    }
    for (int i=2;i<=cnt;++i){
        static int x;x=rk[i];
        ans+=(ll)sub[x]*size[x]*(len[x]-len[fa[x]]);
    }printf("%lld\n",ans);
    return 0;
}
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库的get()法来访问目标网址,并将目标文件下载到我们的本地计算机。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程,我们可以使用split()法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值