• Pty的字符串(string)


    题目描述

    在神秘的东方有一棵奇葩的树,它有一个固定的根节点(编号为1)。树的每条边上都是一个字符,字符为a,b,c中的一个,你可以从树上的任意一个点出发,然后沿着远离根的边往下行走,在任意一个节点停止,将你经过的边的字符依次写下来,就能得到一个字符串,例如:

    在这棵树中我们能够得到的字符串是:

    c, cb, ca, a, b, a

    现在pty得到了一棵树和一个字符串S。如果S的一个子串[l,r]和树上某条路径所得到的字符串完全相同,则我们称这个子串和该路径匹配。现在pty想知道,S的所有子串和树上的所有路径的匹配总数是多少?

    输入格式

    第一行:n

    接下来n-1行,每行一个整数fa, 一个字符c,字符和整数之间用一个空格隔开

    第i行fa代表第i号节点的父亲,c表示第i号节点和fa的连边的字符

    最后一行为字符串S

    输出格式

    输出共一行,表示匹配总数,因为评测系统暂未确定,所以C/C++选手请使用cout输出。

    样例

    样例输入

    5
    1 c
    2 b
    1 a
    2 a
    cba

    样例输出

    5

    数据范围与提示

    【样例说明】

    单个字符匹配的对数为4对,两个字符匹配的对数为1对:cb


    solution
     
    广义后缀自动机
    其实是在trie树上建。
    具体看
    记录 val[i] 表示i这个状态所代表的所有串的出现次数和
    val=(max-min)*right
    记sum为i到根的val之和。
    做匹配的时候,我们考虑新加入一个字符。
    假设这个字符的匹配点是i,之前匹配长度为len。
    那么这个字符的答案就是(len-s[fa].max)*ri[i]+s[fa].sum.
    也就是该字符串在这个点的匹配长度,加上父亲点的sum,也就是以这个字符结尾的其他后缀的贡献。
     1 格式化代码 
     2 #include<cstdio>
     3 #include<iostream>
     4 #include<cstdlib>
     5 #include<cstring>
     6 #include<algorithm>
     7 #include<cmath>
     8 #define maxn 800005
     9 #define ll long long
    10 using namespace std;
    11 int n,id[maxn],rt=1,cnt=1,la=1,a[maxn];
    12 struct node{
    13     int nex[26],par,Max,ri;
    14     ll val,sum;
    15 }s[maxn*3];
    16 char ch[maxn*10];
    17 ll tax[maxn];
    18 void ins(int fa,int c,int i){
    19     
    20     int np=++cnt,p=id[fa];s[np].ri=1;
    21     //cout<<i<<' '<<c<<' '<<p<<endl;
    22     s[np].Max=s[la].Max+1;id[i]=la=np;//这里还不是很懂为什么是la
    23     
    24     for(;p&&!s[p].nex[c];p=s[p].par)s[p].nex[c]=np;
    25     if(!p)s[np].par=rt;
    26     else {
    27         int q=s[p].nex[c];
    28         if(s[q].Max==s[p].Max+1)s[np].par=q;
    29         else {
    30             int nq=++cnt;
    31             for(int i=0;i<26;i++)s[nq].nex[i]=s[q].nex[i];
    32             s[nq].par=s[q].par;s[nq].Max=s[p].Max+1;
    33             s[q].par=s[np].par=nq;
    34             for(;s[p].nex[c]==q;p=s[p].par)s[p].nex[c]=nq;
    35         }
    36     }
    37 }
    38 void Calc(){
    39     for(int i=1;i<=cnt;i++)tax[s[i].Max]++;
    40     for(int i=1;i<=cnt;i++)tax[i]+=tax[i-1];
    41     for(int i=1;i<=cnt;i++)a[tax[s[i].Max]--]=i;
    42     for(int i=cnt;i>=1;i--){
    43         int k=a[i],f=s[k].par;
    44         s[f].ri+=s[k].ri;
    45         s[k].val=s[k].ri*(s[k].Max-s[f].Max); 
    46     }
    47     for(int i=1;i<=cnt;i++){
    48         int k=a[i];
    49         s[k].sum=s[k].val+s[s[k].par].sum;
    50     }
    51 }
    52 int main(){
    53     cin>>n;id[1]=1;
    54     for(int i=2,fa;i<=n;i++){
    55         char c;scanf("%d %c",&fa,&c);
    56         ins(fa,c-'a',i);
    57     }
    58     Calc();
    59     scanf("%s",ch);int l=strlen(ch),len=0;
    60     int p=rt;ll ans=0;
    61     for(int i=0;i<l;i++){
    62         for(;!s[p].nex[ch[i]-'a']&&p;p=s[p].par,len=s[p].Max);
    63         if(!p)p=rt,len=0;
    64         else {
    65             p=s[p].nex[ch[i]-'a'];len++;
    66             int f=s[p].par;
    67             ans=ans+s[f].sum+(len-s[f].Max)*s[p].ri;
    68         }
    69     }
    70     cout<<ans<<endl;
    71     return 0;
    72 }
    View Code
  • 相关阅读:
    wcf 基本配置
    Config 代码片段
    常用的中文字体
    C++创建一个新的进程
    C++ 线程学习
    [C++]多线程: 教你写第一个线程
    C++ 多线程编程实例【2个线程模拟卖火车票的小程序】
    C++使用thread类多线程编程
    C++多线程编程(★入门经典实例★)
    C++ 多线程
  • 原文地址:https://www.cnblogs.com/liankewei/p/10587560.html
Copyright © 2020-2023  润新知