• (2022牛客多校五)G-KFC Crazy Thursday(二分+哈希)


    题目:

    样例输入: 

    1. 6
    2. kfccfk

    样例输出:

    3 3 3

    题意:给你一个n,以及一个长度为n的字符串,字符串由小写字母组成,然后问里面有多少个分别以k、f、c字母结尾的回文字符串。

    这道题好像标解是回文自动机,但是我不太会,下面我说一下这道题二分+哈希的思路以及Manacher优化的思路。

    看这道题之前大家应该先会求解一个字符串中的最大回文子串,不会的小伙伴可以看这里:求最大回文子串长度_AC__dream的博客-CSDN博客

    首先可以确定的一点是所有的回文串都会有中心字符,如果回文子串长度为奇数,那么就只有一个中心字符,否则就有两个中心字符,所以我们可以枚举以每个字符作为中心时的最长回文串,然后再从中找到以k、f、c结尾的回文串,方法就很简单了,就是说假如我们现在以字符x为中心的最长回文串长度是11,这个长度为11的回文串里面有8个f,那么显然就会有4个以f作为结尾的回文串,毕竟回文串是对称的,所以我们就是这个思路,枚举一下以每个字母作为中心的回文串,事先处理一下k、f、c的数目前缀和,这样可以O(1)判断区间[l,r]里面某个特殊字母出现的次数。注意还有偶数的情况,这个时候就需要枚举一下两个中心点中的一个,左边还是右边都可以,需要注意的就是不存在的情况,就是说两个中心点字母不相同的时候是不存在的情况,二分+哈希求解回文子串长度的方法我已经在之前博客中介绍过了,上面附有博客地址,具体细节见代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. using namespace std;
    10. const int N=5e5+10;
    11. typedef unsigned long long ull;
    12. ull p[N],sum1[N],sum2[N];
    13. int sk[N],sf[N],sc[N];//sx[i]标记前i个位置有多少个字符x
    14. char s[N];
    15. ull get(int l,int r,int op)
    16. {
    17. if(op==1)
    18. return sum1[r]-sum1[l-1]*p[r-l+1];
    19. else
    20. return sum2[l]-sum2[r+1]*p[r-l+1];
    21. }
    22. int main()
    23. {
    24. int n;
    25. p[0]=1;
    26. for(int i=1;i
    27. p[i]=p[i-1]*131;
    28. while(scanf("%d",&n)!=EOF)
    29. {
    30. scanf("%s",s+1);
    31. for(int i=1;i<=n;i++)
    32. {
    33. sum1[i]=sum1[i-1]*131+s[i];
    34. if(s[i]=='k') sk[i]=sk[i-1]+1;
    35. else sk[i]=sk[i-1];
    36. if(s[i]=='f') sf[i]=sf[i-1]+1;
    37. else sf[i]=sf[i-1];
    38. if(s[i]=='c') sc[i]=sc[i-1]+1;
    39. else sc[i]=sc[i-1];
    40. }
    41. sum2[n+1]=0;
    42. for(int i=n;i>=1;i--)
    43. sum2[i]=sum2[i+1]*131+s[i];
    44. long long ansk,ansf,ansc;
    45. ansk=ansf=ansc=0;
    46. for(int i=1;i<=n;i++)//枚举长度为奇数情况下第i个位置为中心点的回文子串数目
    47. {
    48. int l=0,r=min(n-i,i-1);
    49. while(l
    50. {
    51. int mid=l+r+1>>1;
    52. if(get(i-mid,i+mid,1)==get(i-mid,i+mid,2)) l=mid;
    53. else r=mid-1;
    54. }
    55. ansk+=sk[i+l]-sk[i-1];
    56. ansf+=sf[i+l]-sf[i-1];
    57. ansc+=sc[i+l]-sc[i-1];
    58. }
    59. for(int i=1;i<=n;i++)//枚举长度为偶数情况下第i个位置为两个中心点左边的那个中心点的回文子串数目
    60. {
    61. int l=0,r=min(n-i,i);
    62. while(l
    63. {
    64. int mid=l+r+1>>1;
    65. if(get(i-mid+1,i+mid,1)==get(i-mid+1,i+mid,2)) l=mid;
    66. else r=mid-1;
    67. }
    68. if(l==0) continue;
    69. ansk+=(sk[i+l]-sk[i-l])/2;
    70. ansf+=(sf[i+l]-sf[i-l])/2;
    71. ansc+=(sc[i+l]-sc[i-l])/2;
    72. }
    73. printf("%lld %lld %lld\n",ansk,ansf,ansc);
    74. }
    75. return 0;
    76. }

    Manacher就是对上述查找最大回文子串的一个优化,上述的复杂度是O(nlogn),而用Manacher优化后的复杂度就是O(n)。只是把找以每个字符为中心的最大回文子串方法进行了优化,不会Manacher算法的小伙伴可以看下这里:Manacher(求解最长回文子串)_AC__dream的博客-CSDN博客

    下面就直接附上代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. using namespace std;
    10. const int N=2e6+100;
    11. char s[N];
    12. int p[N],mx,id;
    13. int sk[N],sf[N],sc[N];
    14. int main()
    15. {
    16. int n;
    17. cin>>n;
    18. scanf("%s",s+1);
    19. for(int i=n;i>=0;i--)
    20. {
    21. s[i*2]=s[i];
    22. s[i*2+1]='#';
    23. }
    24. s[0]='$';
    25. for(int i=1;i<=2*n+1;i++)
    26. {
    27. if(s[i]=='k') sk[i]=sk[i-1]+1;
    28. else sk[i]=sk[i-1];
    29. if(s[i]=='f') sf[i]=sf[i-1]+1;
    30. else sf[i]=sf[i-1];
    31. if(s[i]=='c') sc[i]=sc[i-1]+1;
    32. else sc[i]=sc[i-1];
    33. }
    34. int ansk=0,ansf=0,ansc=0;
    35. for(int i=1;i<=2*n+1;i++)
    36. {
    37. if(imin(p[2*id-i],mx-i);
    38. else p[i]=1;
    39. while(i>p[i]&&s[i-p[i]]==s[i+p[i]]) p[i]++;
    40. if(i+p[i]>mx)
    41. {
    42. mx=i+p[i];
    43. id=i;
    44. }
    45. ansk+=(sk[i+p[i]-1]-sk[i-p[i]]+1)/2;
    46. ansf+=(sf[i+p[i]-1]-sf[i-p[i]]+1)/2;
    47. ansc+=(sc[i+p[i]-1]-sc[i-p[i]]+1)/2;
    48. }
    49. printf("%d %d %d",ansk,ansf,ansc);
    50. return 0;
    51. }

  • 相关阅读:
    游戏中的各种简单实现方案
    Linux Ftrace介绍
    研发效能DevOps: OpenEuler 部署 drone 持续集成平台
    记一次实战 Shiro反序列化内网上线
    一看就会的Java方法
    spring boot配置ssl证书,支持https访问
    C++Primer笔记:第一章:开始
    大众CEO提前“毕业”,马斯克:软件是通向未来的关键
    数字图像处理(九)双边滤波
    工业化生产预测(xgboost)(笔记版)
  • 原文地址:https://blog.csdn.net/AC__dream/article/details/126131062