UVA 7041 The Problem to Slow Down You (回文树)

The Problem to Slow Down You

After finishing his homework, our problem setter Federmann decided to kill time by hanging around online. He found a cool chat room that discusses competitive programming. Federmann has already joined lot of such chat rooms, but this one is special. Once he entered the chat room, he noticed that there is an announcement saying “We forbid off-topic messages!”. Federmann thinks that’s quite un- usual, he decided to sit down and join the talk. After watching people discussing different programming challenges for a while, he found an interesting message saying “No, Federmann won’t prepare another problem about strings this year.”
“Oh, why do you guys think about that?” Federmann smiled. “Don’t they know I have an Edward number (the Edward number is something like Erds number, among problem setters) of 3?”
He then thought about something about palindrome, given two strings A and B, what is the number of their common palindrome substrings? The amount of common palindrome substrings between two strings is defined as the number of quadruple (p, q, s, t), which satisfies that:
1. 1 ≤ p,q ≤ length(A), 1 ≤ s,t ≤ length(B), p ≤ q and s ≤ t. Here length(A) means the length of string A.
2. Ap..q = Bs..t
3. Ap..q is palindrome. (palindrome string is the string that reads the same forward or backward)
For example, (1, 3, 1, 3) and (1, 3, 3, 5) are both considered as a valid common palindrome substring between “aba” and “ababa”.
Federmann is excited about his new task, and he is just too lazy to write solutions, help him.
Input
The first line of the input gives the number of test cases, T. T test cases follow. For each test case, the first line contains a string A and the second line contains a string B. The length of A, B will not exceed 200000.
It is guaranteed the input file will be smaller than 8 MB.
Output
For each test case, output one line containing ‘Case #x: y’, where x is the test case number (starting from 1) and y is the number of common palindrome substrings of A and B.
Sample Input
3
abacab
abccab
faultydogeuniversity
hasnopalindromeatall
abbacabbaccab
youmayexpectedstrongsamplesbutnow
Sample Output
Case #1: 12
Case #2: 20
Case #3: 18

回文树模板题。题意是给两个串 问子串是回文串的有多少对。子串必须一个在串1中一个在串2中。先把第一个串的回文树跑出来,在这颗树的基础上在用串2跑。最后cnt1[i]*cnt2[i]求个和就可以过。

#include "cstring"
#include "cstdio"
#include "string.h"
#include "iostream"
#include "cmath"
using namespace std;
const int MAXN = 400005 ;
const int N = 26 ;
char str1[MAXN];
char str2[MAXN];

struct Palindromic_Tree {
    int slen,slen1;
    int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
    int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
    long long cnt[MAXN] ;
    long long cnt1[MAXN] ;
    int num[MAXN] ;
    int len[MAXN] ;//len[i]表示节点i表示的回文串的长度
    int S[MAXN] ;//存放添加的字符
    int last ;//指向上一个字符所在的节点,方便下一次add
    int n ;//字符数组指针
    int p ;//节点指针
    int newnode ( int l ) {//新建节点
        for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
        cnt[p] = 0 ;
        num[p] = 0 ;
        len[p] = l ;
        return p ++ ;
    }
    void init () {//初始化
        for(int i=0;i<=slen+slen1+4;i++)
        {
            cnt1[i]=cnt[i]=0;
        }

        p = 0 ;
        newnode (  0 ) ;
        newnode ( -1 ) ;
        last = 0 ;
        n = 0 ;
        S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
        fail[0] = 1 ;
    }
    void init1()
    {
        last=0;
        n=0;
        S[n]=-1;
        fail[n]=1;
    }
    int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
        while(S[n-len[x]- 1]!= S[n])
            x = fail[x] ;
        return x ;
    }

    void add ( int c ) {
        c -= 'a' ;
        S[++ n] = c ;
        int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
        if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
            int now = newnode ( len[cur] + 2 ) ;//新建节点
            fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
            next[cur][c] = now ;
            num[now] = num[fail[now]] + 1 ;
        }
        last = next[cur][c] ;
        cnt[last] ++ ;
    }

    void add1(int c)
    {
        c -= 'a' ;
        S[++ n] = c ;
        int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
        if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
            int now = newnode ( len[cur] + 2 ) ;//新建节点
            fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
            next[cur][c] = now ;
            num[now] = num[fail[now]] + 1 ;
        }
        last = next[cur][c] ;
        cnt1[last] ++ ;

    }

    void count () {
        for ( int i = p - 1 ; i >= 0 ; -- i )
            cnt[fail[i]] += cnt[i] ;
        //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
    }
    void count1()
    {
        for ( int i = p - 1 ; i >= 0 ; -- i )
            cnt1[fail[i]] += cnt1[i] ;
    }
} tree;

int main()
{
    int n;
    scanf("%d",&n);
    int k=1;
    while(n--)
    {
        scanf("%s%s",str1,str2);
        tree.init();
        tree.slen=strlen(str1);
        tree.slen1=strlen(str2);
        for(int i=0;i<tree.slen;i++)
            tree.add(str1[i]);
        tree.count();
        tree.init1();
        for(int i=0;i<tree.slen1;i++)
            tree.add1(str2[i]);
        tree.count1();
        long long ans=0;
        for(int i=2;i<=tree.p-1;i++)
        {
            ans+=tree.cnt[i]*tree.cnt1[i];
        }
        printf("Case #%d: %lld\n",k++,ans);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章