zoukankan      html  css  js  c++  java
  • HDU6599多校第二场 I Love Palindrome String(回文树+马拉车)

    I Love Palindrome String

    Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
    Total Submission(s): 818    Accepted Submission(s): 329


    Problem Description
    You are given a string S=s1s2..s|S| containing only lowercase English letters. For each integer i[1,|S|] , please output how many substrings slsl+1...sr satisfy the following conditions:

      rl+1 equals to i.

      The substring slsl+1...sr is a palindrome string.

      slsl+1...s(l+r)/2 is a palindrome string too.

    |S| denotes the length of string S.

    A palindrome string is a sequence of characters which reads the same backward as forward, such as madam or racecar or abba
     
    Input
    There are multiple test cases.

    Each case starts with a line containing a string S(1|S|3×105) containing only lowercase English letters.

    It is guaranteed that the sum of |S| in all test cases is no larger than 4×106.
     
    Output
    For each test case, output one line containing |S| integers. Any two adjacent integers are separated by a space.
     
    Sample Input
    abababa
     
    Sample Output
    7 0 0 0 3 0 0
     
    Source
     
    题意:给你一个字符串,然后叫你求,对于每一个长度i-len,问有多少个,回文串的前一半也是回文串。
     
    这题的话,我们需要用到一个回文树(也叫回文自动机)来处理,回文树可以在O(nlogm)(其实logm很小,可以忽略,复杂度相当与O(n))的时间里找到所以的回文串,并统计
    出每一个回文串的长度
    了解回文树的都知道,回文树的没一个结点代表的都是一个回文串,由于我们要判断这个回文串的一半是否为回文串,这么我们可以用hash或manachar处理,我选择的用manachar。
    在回文树的结点信息上再添加一个保存在原来字符串位置的区间(l,r),为什么要添加这个呢?因为我们用manachar来判断的回文的话需要用到回文中心,这里我们需要知道的是原回文子字符串str[l~r)]在转换后字符串的回文中心位置(l+r+2),这时我们只需要判断这个回文串的一半是否回文manachar_len[l +(l+r)/2  + 2]的长度是否满足大于等于它的回文长度/2.
    回文树不懂的话,代码有注释,可能看着相对容易理解一点(感觉fail数组那不好理解,学了好久好久)
     
    这里用hash判断回文的话似乎更简单一些,比如当前选择的结点为ababa我们用hash判断长度为奇数判断hash[l,mid]和hash[mid, r]是否相等就好了
    偶长度判断 hash[l,mid]和hash[mid+1, r]
     
      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 const int MAXN = 300005;
      4 char tmp[MAXN * 2];
      5 int manachar_len[MAXN*2];
      6 struct Palindromic_Tree
      7 {
      8     int next[MAXN][26];
      9     int fail[MAXN];///当前节点的最长回文后缀串的结点(不包括自己),也就是匹配不成功应该跳转的地方,
     10     int cnt[MAXN];
     11     int len[MAXN];///回文串的长度
     12     int num[MAXN];
     13     int s[MAXN];///加入的字符
     14     int p, n, last;///n字符的指针,p表示结点,一个节点代表一个回文串,last指向新添加一个字母后所形成的最长回文串表示的节点
     15     struct node
     16     {
     17         int l, r;
     18     } P[MAXN];
     19     int newNode(int length)
     20     {
     21         for (int i = 0; i < 26; i++)
     22             next[p][i] = 0;
     23         num[p] = cnt[p] = 0;///记录信息
     24         len[p] = length;
     25         P[p].l = 0;
     26         P[p].r = 0;
     27         return p++;
     28     }
     29     void init()
     30     {
     31         p = 0;
     32         newNode(0);//0 号节点代表偶数根,长度为0 , 1号节点代表奇根,长度为 -1
     33         newNode(-1);
     34         last = n = 0;///刚开始时,所有节点指向0号节点
     35         fail[0] = 1;///0号节点的fail 指针指向  1(奇根) 号节点
     36         s[0] = -1;///开头放一个字符集中没有的字符,减少特判
     37     }
     38     int get_fail(int x)//找到第一个使得S[n - len[last] - 1] == S[n]的last
     39     {
     40         while(s[n - len[x] - 1] != s[n])
     41             x = fail[x];
     42         return x;
     43     }
     44     void add(int c)
     45     {
     46         int x = c-'a';
     47         s[++ n] = x;
     48         int cur = get_fail(last);///cur节点相当于now节点的父亲结点 cur->now
     49         if(!next[cur][x])
     50         {
     51             int now = newNode(len[cur] + 2);
     52             fail[now] = next[ get_fail( fail[cur] ) ][ x ];///fail[cur]得到cur的最长回文后缀,
     53             ///再通过这个回文后缀找到使得S[n - len[last] - 1] == S[n]的last结点
     54             ///比如abbaabba  cur指向的结点为bbaabb,这个节点的最长回文后缀为bb,那么看bb['a'[bb]'x']
     55             ///看'x'是否和'a'相等,如果相等,那么fail指正指向['a'[bb]'x']这个节点,否则的话就不断去缩小bb
     56             ///回文后缀,直到到达 1 号节点,fail 指向长度为 1 的 'x' 节点;
     57             next[cur][x] = now;
     58             num[now] = num[fail[now]] + 1;
     59         }
     60         last = next[cur][x];
     61         P[last].l = n -1 - len[last] + 1;
     62         P[last].r = n - 1;
     63         cnt[last] ++;
     64     }
     65     void count()
     66     {
     67         for(int i = p - 1; i >= 0; i --)
     68         {
     69             /// printf("%d
    ", cnt[i]);
     70             cnt[fail[i]] += cnt[i];
     71         }
     72     }
     73 } AC;
     74 
     75 int init(char st[])
     76 {
     77     int i,str_len=strlen(st);
     78     tmp[0]='@';///加一个特殊字符,防止越界
     79     for(int i = 1; i <= 2*str_len ; i+=2)
     80     {
     81         tmp[i]='#';
     82         tmp[i+1]=st[i/2];
     83     }
     84     tmp[2*str_len+1]='#';
     85     tmp[2*str_len+2]='$';///字符串结尾加一个字符,防止越界
     86     return 2*str_len+1;///返回转换字符串的长度
     87 }
     88 
     89 void manachar(char st[],int str_len)
     90 {
     91     int p_mx=0,ans=0,po=0;///p_mx即为“当前”计算回文串最右边字符的最大值,po为当前该回文字符串的中心
     92     for(int i=1; i<=str_len; i++)
     93     {
     94         if(i<p_mx)
     95             manachar_len[i]=min(p_mx-i,manachar_len[2*po-i]);///po对应最大回文子串中心点的位置
     96         else
     97             manachar_len[i]=1;
     98         while(st[i-manachar_len[i]]==st[i+manachar_len[i]])
     99             manachar_len[i]++;
    100         if(manachar_len[i]+i>p_mx)
    101         {
    102             po=i;
    103             p_mx=i+manachar_len[i];
    104         }
    105         ans=max(ans,manachar_len[i]);
    106     }
    107    // return ans-1;///返回manachar_len[i]中的最大值减一即为原串的最长回文子串的长度
    108 }
    109 char str[MAXN];
    110 int len_ans[MAXN];
    111 int main()
    112 {
    113     while(~scanf("%s",str))
    114     { 
    115         int len_tmp = init(str);
    116         manachar(tmp, len_tmp);
    117         memset(len_ans, 0,sizeof(len_ans));
    118         int len = strlen(str);
    119         AC.init();
    120         for(int i = 0; i < len; i++)
    121         {
    122             AC.add(str[i]);
    123         }
    124         AC.count();
    125         long long ans = 1;
    126         for(int i = 0; i < AC.p; i++)
    127         {
    128             if(manachar_len[AC.P[i].l+(AC.P[i].l+AC.P[i].r)/2+2] - 1 >= AC.len[i]/2)
    129             {
    130                 len_ans[AC.len[i]] += AC.cnt[i];
    131             }
    132         }
    133         for(int i = 1; i <= len; i++)
    134         {
    135             if(i == 1 )
    136             printf("%d",len_ans[i]);
    137             else
    138               printf(" %d",len_ans[i]);
    139         }
    140         puts("");
    141     }
    142 
    143     return 0;
    144 }
     
  • 相关阅读:
    【React】哪些数据应该放到state中?
    React组件生命周期及组件之间的通信
    【Flex布局教程】语法篇
    jQuery动态创建二级下拉菜单
    Web前端工程师成长之路——知识汇总
    Jpcap使用指南
    安装/重装tomcat(组图)
    贪吃蛇小游戏(含详细思路及源代码)
    servlet中如何操作数据库
    svn的使用方法
  • 原文地址:https://www.cnblogs.com/yuanlinghao/p/11268877.html
Copyright © 2011-2022 走看看