zoukankan      html  css  js  c++  java
  • 【题解】Antisymmetry

    题目大意

            对于一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如00001111和010101就是反对称的,1001就不是。

            现在给出一个长度为N的01字符串,求它有多少个子串是反对称的。

     

    输入样例

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

    输出样例

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

    输入样例

    8

    11001011

    输出样例

    7

    题解

            最简单的方法就是二分+Hash了。

            我们可以想,对于每个点$i$,如果字符串$[i - j + 1...i + j]$为反对称的,则对于满足$1 leqslant k < j$的$k$,都有字符串$[i - k + 1...i + k]$为反对称的。

            根据这个单调性,我们只需要枚举$i$,然后二分$j$即可。

    #include <iostream>
    #include <cstdio>
    
    #define MAX_N (500000 + 5)
    
    using namespace std;
    
    typedef unsigned long long ull;
    typedef const unsigned long long cull;
    int n;
    char s[MAX_N];
    cull b = 3;
    ull h1[MAX_N], h2[MAX_N], pb[MAX_N];
    int ans;
    
    int main()
    {
        scanf("%d%s", &n, s + 1);
        pb[0] = 1;
        for(register int i = 1, j = n; i <= n; ++i, --j) 
        {
            h1[i] = h1[i - 1] * b + (s[i] == '1') + 1;
            h2[j] = h2[j + 1] * b + (s[j] == '0') + 1;
            pb[i] = pb[i - 1] * b;
        }
        int lt, rt, mid;
        for(register int i = 1; i <= n; ++i) 
        {
            lt = 1;
            rt = min(i, n - i);
            while(lt <= rt) 
            {
                mid = lt + rt >> 1;
                if(h1[i + mid] - h1[i - mid] * pb[mid << 1] != h2[i - mid + 1] - h2[i + mid + 1] * pb[mid << 1]) rt = mid - 1;
                else lt = mid + 1;
            }
            ans += rt;
        }
        printf("%d", ans);
        return 0;
    }
    参考程序(Hash)

            当然,我们也可以直接用manacher做。

            其实反对称就类似回文,这里的反对称其实可以理解为对于每个字符串,左半边异或后等于右半边,我们把manacher稍微改一下即可。

            注意,这里不能随枚举奇数长度的子串,因为奇数长度本身就是不可能反对称的,这样子可能会影响偶数长度子串的判断。

    #include <iostream>
    #include <cstdio>
    
    #define MAX_N (500000 + 5)
    
    using namespace std;
    
    int n;
    char s[MAX_N];
    int len;
    char ns[MAX_N << 1];
    int p[MAX_N << 1];
    char to[130];
    long long ans;
    
    int main()
    {
        scanf("%d%s", &n, s + 1);
        len = n << 1 | 1;
        ns[0] = '~';
        ns[len + 1] = '^';
        ns[1] = '#'; 
        for(register int i = 1; i <= n; ++i) 
        {
            ns[i << 1] = s[i];
            ns[i << 1 | 1] = '#';
        }
        to['0'] = '1';
        to['1'] = '0';
        to['#'] = '#';
        to['~'] = '~';
        to['^'] = '^';
        int rt = 0, mid = 0;
        for(register int i = 1; i <= len; i += 2) 
        { 
            if(i < rt) p[i] = min(rt - i + 1, p[(mid << 1) - i]);
            else p[i] = 1;
            while(ns[i + p[i]] == to[ns[i - p[i]]]) ++p[i];
            if(i + p[i] - 1 > rt) 
            {
                rt = i + p[i] - 1;
                mid = i;
            }
            ans += p[i] >> 1;
        }
        printf("%lld", ans);
        return 0;
    }
    参考程序(Manacher)
  • 相关阅读:
    linux 下spyder安装
    【C++】fill函数,fill与memset函数的区别
    【tensorflow使用笔记一】:安装linux下tensorflow环境的问题
    leetcode 49 字母异位词分组
    leetcode 1014. 在 D 天内送达包裹的能力
    【C++进阶:STL常见性质3】
    【C++进阶:STL常见性质2】
    【C++进阶:STL常见性质】
    【C++进阶:移位运算符的用法】
    面向对象之静态方法
  • 原文地址:https://www.cnblogs.com/kcn999/p/11228578.html
Copyright © 2011-2022 走看看