zoukankan      html  css  js  c++  java
  • [bzoj2084][POI2010]Antisymmetry

    刚刚学习了manacher算法,是在O(n)时间内求以每个字符或每两个字符为中心的最长回文子串长度的一个算法。

    然后做了一道POI的题,一道好题。看了题解再做的,有点可惜这道题了。


    Description

    对于一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如00001111和010101就是反对称的,1001就不是。
    现在给出一个长度为N的01字符串,求它有多少个子串是反对称的。

    Input

    第一行一个正整数N (N <= 500,000)。第二行一个长度为N的01字符串。

    Output

    一个正整数,表示反对称子串的个数。

    Sample Input

    8
    11001011

    Sample Output

    7

    HINT

    7个反对称子串分别是:01(出现两次), 10(出现两次), 0101, 1100和001011


     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cmath>
     5 #define mxn 1000010
     6 using namespace std;
     7 int str[mxn],tmp[mxn],f[mxn];
     8 char s[mxn];
     9 void init(int len){
    10     for(int i=0;i<len;i++)
    11     str[i]=s[i]-'0';
    12     tmp[0]=3;
    13     for(int i=1;i<=2*len;i+=2){
    14         tmp[i]=2;
    15         tmp[i+1]=str[i/2];
    16     }
    17     tmp[2*len+1]=2;  
    18     tmp[2*len+2]=3;
    19 }
    20 bool check(int x,int y){
    21     return ((tmp[x]^tmp[y])==1)||(tmp[x]==2&&tmp[y]==2);
    22 }
    23 void manacher(int len){
    24      int mx=0,po=0;
    25      for(int i=1;i<=len;i+=2){
    26          if(mx>i)
    27          f[i]=min(mx-i,f[2*po-i]);
    28          else
    29          f[i]=1;
    30          while(check(i-f[i],i+f[i]))
    31          f[i]++;
    32          if(f[i]+i>mx){
    33              mx=f[i]+i;
    34              po=i;
    35          }
    36      }
    37      long long ans=0;
    38      for(int i=1;i<=len;i+=2)
    39      ans+=(f[i]-1)>>1;
    40      printf("%lld",ans);
    41 }
    42 int main(){
    43     int n;scanf("%d",&n);
    44     scanf("%s",s);init(n);manacher(2*n+1);
    45     return 0;
    46 }
    View Code

     仔细考虑一个反对称子串的性质:

    1、反对称子串一定是偶数串。因为不论0还是1,取反后都不会和自己相等。对于一个奇数串,正中间的数取反对称还是和自己是对应。

    2、一个串是反对称串,它的偶数子串也一定是反对称串。显然。

    3、以正中间两个数中间的空为对称轴,整个反对称串一定是0与1相对的。

    那么就很容易想到,跑一遍manacher,但求得不是回文串,而是反对称串。只要把判断两个字符是否相等改为判断两个数是否互反即可。如果碰到‘#’就还是需要相等。

    预处理往串里填‘#’还是需要的,而且这回只需要跑以‘#’为中心的串了。因为只有以‘#’为中心的串在原串中才是偶数串。

    *不仅是只需要跑‘#’,而且是只能跑‘#’。0,1为中心是不能跑的,跑了会出错。

    ans的求解就是每个‘#’的f(rad)值减一除以二求和。

    复杂度就是O(n)。

    2017-06-29

  • 相关阅读:
    事件总线Guava EventBus
    DDD—实体和值对象
    DDD—子域和限界上下文
    DDD—什么是领域驱动设计
    DDD—微服务,中台建设为什么需要领域驱动设计
    RabbitMQ 中的 7 种队列模式
    10w 行级别数据的 Excel 导入优化记录
    Java 反射是什么?
    21 条常用 Linux 命令
    一个 java 文件的执行过程
  • 原文地址:https://www.cnblogs.com/orzzz/p/7096315.html
Copyright © 2011-2022 走看看