hdu 6599 I Love Palindrome String 回文自动机

hdu 6599 I Love Palindrome String 回文自动机

当个回文自动机的模板

题意

给一个串S,求长度为i的“特殊回文串”个数,“特殊回文串”要求是回文串,并且自己的一半也是回文串。

思路

求回文串个数相关,优先考虑使用回文自动机,“特殊回文串”要求是回文串,并且自己的一半也是回文串,显然就是fail树扒出来dfs搞一下,记录dfs路径上有没有长度一半的回文串即可。
PS:因为fail指针的意义是最长后缀回文,反过来路径显然是当前节点的回文后缀。

代码

#pragma comment (linker,"/STACK:102400000,102400000")
#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define PB push_back
#define LL long long
#define pii pair<int,int>
#define MEM(x,y) memset(x,y,sizeof(x))
#define bug(x) cout<<"debug "#x" is "<<x<<endl;
#define FIO ios::sync_with_stdio(false);
#define ALL(x) x.begin(),x.end()

class PA {//回文自动机
    private:
    //fail 指针指向最长后缀回文状态,可以构建树
    struct Node {
        int len,cnt,ptr[26], fail;  //必要时候 ptr 改成 map
        Node(int len = 0) :cnt(0), len(len), fail(0) { memset(ptr, 0, sizeof(ptr)); }
    };
    vector<Node> nd;
    vector<int> idx;
    int cur;  //当前指针停留的位置,即最后插入字符所对应的节点
    string s;
    int getfail(int x){
        //沿着fail指针找到最长回文后缀(暴力)
        while (s[s.size()-1-nd[x].len-1] != s[s.size()-1]) x = nd[x].fail;
        return x;
    }
    void get_count(){//父亲统计cnt的时候加上儿子的
    for(int i=nd.size()-1;i>=2;i--)
        nd[nd[i].fail].cnt+=nd[i].cnt;
    }
    public:
    PA() :  cur(0){
        nd.PB(Node(0));
        nd[nd.size()-1].fail = 1;
        nd.PB(Node(-1));
        nd[nd.size()-1].fail = 0;
        s="$";
    }
    void extend(char c) {
        s += c;
        int now = getfail(cur);  //找到插入的位置
        if (!nd[now].ptr[c - 'a']){
        //若没有这个节点,则新建并求出它的fail指针
          nd.PB(Node(nd[now].len + 2));
          nd[nd.size()-1].fail = nd[getfail(nd[now].fail)].ptr[c - 'a'];  //新节点的失败指针指向回文后缀
          nd[now].ptr[c - 'a'] = nd.size()-1;   //
        }
        cur = nd[now].ptr[c - 'a'];
        idx.PB(cur);
        ++nd[cur].cnt;
    }
    int get_l(int x){//以x为右端点的最长回文子串的左端点
        return x-nd[idx[x]].len+1;
    }

    void dfs(int x,vector<int> &ans,vector<vector<int>> &E,vector<int> &len,vector<int> &ok){
        if(nd[x].len!=-1)len[nd[x].len]=1;
        if(len[(nd[x].len+1)/2]) ok[x]=1;
        for(int c:E[x])if(c!=0)dfs(c,ans,E,len,ok);
        if(nd[x].len!=-1)len[nd[x].len]=0;
    }
    void addedge(int u,int v,vector<vector<int>> &E){E[u].PB(v);};
    void get_ans(int n){
        vector<int> ans(n+1,0);
        vector<int> len(n+1,0);
        vector<int> ok(nd.size(),0);
        vector<vector<int>> E(nd.size());
        for(int i=0;i<nd.size();i++)addedge(nd[i].fail,i,E);
        dfs(0,ans,E,len,ok);
        get_count();
        for(int i=nd.size()-1;i>=2;i--)if(ok[i])ans[nd[i].len]+=nd[i].cnt;
        for(int i=1;i<=n;i++) cout<<ans[i]<<" \n"[i==n];
    }

};

int main(){
    FIO;
    string s;
    while(cin>>s){
        PA A;
        for(char c:s) A.extend(c);
        A.get_ans(s.size());
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zhangxianlong/p/11600157.html

时间: 2024-11-05 00:40:33

hdu 6599 I Love Palindrome String 回文自动机的相关文章

HDU 6599 I Love Palindrome String (回文树+hash)

题意 找如下子串的个数: (l,r)是回文串,并且(l,(l+r)/2)也是回文串 思路 本来写了个回文树+dfs+hash,由于用了map所以T了 后来发现既然该子串和该子串的前半部分都是回文串,所以该子串的前半部分和后半部分是本质相同的! 于是这个log就去掉了 代码 #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring>

【CF932G】Palindrome Partition 回文自动机

[CF932G]Palindrome Partition 题意:给你一个字符串s,问你有多少种方式,可以将s分割成k个子串,设k个子串是$x_1x_2...x_k$,满足$x_1=x_k,x_2=x_{k-1}...x_i=x{k-i+1}$. $|s|\le 10^6$ 题解:设字符串的长度为n,考虑字符串$T=s_1s_ns_2s_{n-1}...$.问题就转化成了:求将原串划分成若干个长度为偶数的回文子串的方案数. 首先我们有一种暴力的想法,设f[i]表示将前i个字符分成若干个回文子串的方

HDU 5157 Harry and magic string(回文树)

Harry and magic string Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 223    Accepted Submission(s): 110 Problem Description Harry got a string T, he wanted to know the number of T's disjoint

LeetCode Valid Palindrome 有效回文(字符串)

1 class Solution { 2 public: 3 bool isPalindrome(string s) { 4 if(s=="") return true; 5 if(s.length()==1) return true; //单个字符,对称 6 char *p,*q; 7 p=&s[0]; //p指向开头 8 q=&s[s.length()-1]; //q指向末尾 9 while(p!=q){ 10 //测试字符串里是否有字母或数字,若没有,则立刻返回

Validate Palindrome 验证回文字符串

Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases. For example, "race a car" is not a palindrome. Note:Have you consider that the string might be empty? This is a good question to ask du

HDU 5371(Hotaru&#39;s problem-2次回文串)

Hotaru's problem Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2432    Accepted Submission(s): 841 Problem Description Hotaru Ichijou recently is addicated to math problems. Now she is playin

WHU 583 Palindrome ( 回文自动机 &amp;&amp; 本质不同的回文串的个数 )

题目链接 题意 : 给你一个串.要你将其划分成两个串.使得左边的串的本质不同回文子串的个数是右边串的两倍.对于每一个这样子的划分.其对答案的贡献就是左边串的长度.现在要你找出所有这样子的划分.并将贡献乘起来.答案 mod 1e9+7 分析 : 从左到右跑一边回文自动机.对于每个前缀 能够得出其有多少个本质不同的回文子串 本质不同的回文子串的个数实际上就是自动机节点数 - 2 那么跑一遍前缀之后我们能得到所有可作为左边部分串的本质不同回文子串的个数 因为是回文串.所以我们倒着跑一遍.就同样能得到作

回文自动机刷题总结

最长双回文串 裸的回文自动机,将串reverse再插入一遍即可. 双倍回文 这题可以只维护偶回文串然后疯狂加特判判掉奇串 回文自动机,再多维护一个trans指针,指向trans[x]表示长度小于len[x]/2的最长的回文后缀 trans指针可以从父亲(不是fail)的trans指针求出. 其实还可以直接建完自动机后在fail树(即把fail指针当作父亲边构成的树)上开桶dfs 1 #include<bits/stdc++.h> 2 #define N 500050 3 using names

[BZOJ4044]Virus synthesis 回文自动机的DP

4044: [Cerc2014] Virus synthesis Time Limit: 20 Sec  Memory Limit: 128 MB Description Viruses are usually bad for your health. How about fighting them with... other viruses? In this problem, you need to find out how to synthesize such good viruses. W