• [manacher/二分+哈希]KFC Crazy Thursday 2022牛客多校第5场 G


    题目描述

    One day, NIO found an email on his computer from his friend Kala. He opened the email and found a picture with a large string of 26 lowercase letters. He asked Kala why he had sent him this picture. Kala said it was a challenge he had given to NIO: if NIO could figure out the number of palindromes end with 'k', 'f' and 'c' , he would buy NIO a KFC combo. The clever NIO turned on a AI software and converted all the letters on the image into a text file. NIO promised that he will share the KFC combo with you if you can help him. 

    输入描述:

    There will be multiple test cases.

    The first line a number NNN, denoting the length of the string.

    The second line is a string consists of lower letters 'a' to 'z'.

    1≤N≤5×1051\leq N\leq 5\times 10^51≤N≤5×105

    输出描述:

    A line with 333 numbers, denoting the number of palindromes, that end with 'k', 'f' and 'c'.

    示例1

    输入

    6
    kfccfk

    输出

    3 3 3

    说明

    For the first 'k', 1 palindrome.

    For the second 'k', 2 palindromes.

    For the first 'f', 1 palindrome.

    For the second 'f', 2 palindromes.

    For the first 'c', 1 palindrome.

    For the second 'c', 2 palindromes.

    备注:

    Palindromes with length greater or equal to one is considered. For example, 'k' is a palindrome. 

    题意: 给出一个长度为n的字符串,分别求以'k'、'f'和'c'结尾的回文子串个数。

    分析: 听说这题是道回文自动机模板题,但是本人太菜了,只会另外两种方法,一种是二分+哈希,时间复杂度为O(nlogn),一种是manacher算法,时间复杂度为O(n)。由于二分+哈希思路比较明显而且代码也很好写,就只说下解题思路,以'k'为例,首先需要维护出来前i个字符中有多少个'k'出现,这点是为了后面快速得到区间内'k'的出现次数,之后的步骤就和求最长回文子串一样了,O(n)枚举回文中心,对于每个中心O(logn)二分得到最长回文长度,然后这个中心位置对于答案的贡献就是最长回文串中'k'出现次数+1再除2,为什么要+1?这主要是考虑到中心位置字符本身就是'k'的情况。

    第二种方法是manacher算法,这个算法一般用来求最长回文子串长度,但是这里对其稍加修改也可以解决该问题。我们知道manacher算法是一个优化版的中心扩展法,同时每个位置都有一个最远延伸长度p[i],另外还需要维护一个最远回文串的中心以及最右边界r,当前位置p[i]的值是可以通过前面某个镜像的位置更新的,所以需要维护一个数组,记录下来第i个位置扩展区间内合法的子串数,之后再以中心扩展法暴力匹配时,也可以记录下来匹配的'k'个数,二者加和就是当前位置扩展区间内合法的子串数,但是其实还有一个问题,那就是镜像点扩展长度超出了最右边界r,此时当前位置的扩展长度与镜像点扩展长度p[i]并不相同了,镜像点的p[i]范围更大,其中记录的合法子串可能根本不会在当前位置出现,所以对于超出最右边界的那些点还需要枚举一遍,要减掉这时候出现的合法子串,最终答案就是每个位置的合法子串数加和。

    具体代码如下:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #define int long long
    7. using namespace std;
    8. int p[4000005];//p[i]记录s[i]能单侧延伸多长
    9. char s[4000005];
    10. int numk[4000005], numf[4000005], numc[4000005];
    11. int manacher(){
    12. int r = 0;//最右边界
    13. int c = 0;//当前中心
    14. int len = strlen(s)-1, ans = 0;
    15. for(int i = 1; i <= len; i++){
    16. if(i < r){
    17. p[i] = min(r-i, p[2*c-i]);//点i关于c的对称点2*c-i
    18. numk[i] += numk[2*c-i];
    19. numf[i] += numf[2*c-i];
    20. numc[i] += numc[2*c-i];
    21. if(r-i < p[2*c-i]){
    22. for(int j = r-i; j <= p[2*c-i]; j++){
    23. if(s[2*c-i+j] == s[2*c-i-j]){
    24. if(s[2*c-i+j] == 'k')
    25. numk[i]--;
    26. if(s[2*c-i+j] == 'f')
    27. numf[i]--;
    28. if(s[2*c-i+j] == 'c')
    29. numc[i]--;
    30. }
    31. }
    32. }
    33. }
    34. else
    35. p[i] = 1;//单个字符延伸长度为1
    36. while(s[i+p[i]] == s[i-p[i]]){//中心扩展法,根据需要加条件
    37. if(s[i+p[i]] == 'k')
    38. numk[i]++;
    39. if(s[i+p[i]] == 'f')
    40. numf[i]++;
    41. if(s[i+p[i]] == 'c')
    42. numc[i]++;
    43. p[i]++;
    44. }
    45. if(i+p[i] > r){//更新中心点及最右边界
    46. r = i+p[i];
    47. c = i;
    48. }
    49. ans = max(ans, p[i]);
    50. }
    51. return ans-1;//当前位置的p[i]-1就是该位置能构成的最长回文串
    52. }
    53. signed main(){
    54. //预处理s,处理为%#a#b#c#这样形式
    55. int n;
    56. while(~scanf("%lld", &n)){
    57. getchar();
    58. char ch;
    59. int cnt = 0;
    60. for(int i = 0; i <= 4*n; i++)
    61. numk[i] = numc[i] = numf[i] = 0;
    62. while(isalpha(ch = getchar())){
    63. s[++cnt] = '#';
    64. s[++cnt] = ch;
    65. }
    66. s[0] = '%';
    67. s[++cnt] = '#';
    68. s[++cnt] = '\0';
    69. manacher();
    70. int ansk = 0, ansf = 0, ansc = 0;
    71. for(int i = 0; i <= cnt; i++){
    72. ansk += numk[i];
    73. ansf += numf[i];
    74. ansc += numc[i];
    75. if(s[i] == 'k') ansk++;
    76. if(s[i] == 'f') ansf++;
    77. if(s[i] == 'c') ansc++;
    78. }
    79. printf("%lld %lld %lld\n", ansk, ansf, ansc);
    80. }
    81. return 0;
    82. }

  • 相关阅读:
    计算机网络 数据链路层课后题
    gradle查看依赖树关系&&支持下载
    Cannot read properties of undefined (reading ‘resetFields‘)“ 报错解决
    【C++】超详细入门——lambda表达式
    Ubuntu LabelMe AI 识别
    零基础Linux_14(基础IO_文件)缓冲区+文件系统inode等
    【工作笔记】数据库死锁的排查和数据库锁知识记录
    大数据——Spark SQL
    matlab求解方程组-求解过程中限制解的取值范围
    RAII技术学习
  • 原文地址:https://blog.csdn.net/m0_55982600/article/details/126111646