首页 > 代码库 > [BestCoder Round #3] hdu 4909 String (状压,计数)

[BestCoder Round #3] hdu 4909 String (状压,计数)

String



Problem Description
You hava a non-empty string which consists of lowercase English letters and may contain at most one ‘?‘. Let‘s choose non-empty substring G from S (it can be G = S). A substring of a string is a continuous subsequence of the string. if G contains ‘?‘ then ‘?‘ can be deleted or replaced by one of lowercase english letters. After that if each letter occurs even number of times in G then G is a good substring. Find number of all good substrings.
 

Input
The input consists of an integer T, followed by T lines, each containing a non-empty string. The length of the string doesn‘t exceed 20000.

[Technical Specification]
1 <= T <= 100
 

Output
For each test case, print a single integer which is the number of good substrings of a given string.
 

Sample Input
3 abc?ca aabbcc aaaaa
 

Sample Output
7 6 6
Hint
Good substrings of "abc?ca": "?", "c?", "?c", "c?c", "bc?c", "c?ca", "abc?ca"
 

Source
BestCoder Round #3


解题思路:

题意为给定一个包含小写字母的序列,其中可能有一个‘?‘,也可能没有,有的话最多只有一个,‘?‘可以删除,也可以替换为任意一个小写字母,问给定的序列中有多少个连续的子序列,保证所有小写字母出现的次数为偶数。

用一个26位二进制数来表示状态,每一位代表一个小写字母,关键是:

当一个状态出现时,后面又加上几个连续的字母,(状态的改变用状态的某一位异或1,)该状态又一次出现了,那么加上的那几个连续字母是符合题意的一个串,只有异或偶数倍某一位的状态才能回到本身, 比如一开始某一位的状态为0,异或一次0^1=1,  1^1=0......  再比如是1,  1^1=0,0^1=1

要求记录每个状态出现的次数,那么26位,状态有 2^26-1个,用map<int,int>hash来模拟数组

hash[x] ,代表状态x出现的次数

对于有出现’?‘的串,可以分为三部分,?之前部分符合题意的串,之后部分符合题意的串,加上?以后符合题意的串,具体思路在代码注释中。

参考:http://blog.csdn.net/accelerator_/article/details/38363245#comments  大哭由于作者文章没有注释,代码看了好半天,才慢慢弄懂。。。。

代码:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <map>
using namespace std;
const int maxn=20010;
char str[maxn];
int cas,n,p,x;
int ans;
map<int,int>hash;//hash[x]表示状态x出现了多少次

int main()
{
    scanf("%d",&cas);
    while(cas--)
    {
        hash.clear();
        scanf("%s",str);
        p=-1;
        n=strlen(str);
        for(int i=0;i<n;i++)
        {
            if(str[i]=='?')
            {
                p=i;
                break;
            }
        }
        ans=0;
        
        if(p==-1)//一个状态出现过,后面又加了几个字母,而这个状态又重新出现了,说明后面加的
        {        //每个字母都是偶数个,因为0^1=1,1^1=0
            x=0;
            hash[0]++;
            for(int i=0;i<n;i++)
            {
                x^=(1<<(str[i]-'a'));
                ans+=hash[x];//前面出现过hash[x]次,加上它
                hash[x]++;
            }
            printf("%d\n",ans);
            continue;
        }
        else
        {
            x=0;//计算'?'前面的串符合题意的有多少个
            hash[0]++;
            for(int i=0;i<p;i++)
            {
                x^=(1<<(str[i]-'a'));
                ans+=hash[x];
                hash[x]++;
            }
            hash.clear();//注意此时清空,后面的串是独立计算的

            x=0;//计算'?'后面的串符合题意的有多少个
            hash[0]++;
            for(int i=p+1;i<n;i++)
            {
                x^=(1<<(str[i]-'a'));
                ans+=hash[x];
                hash[x]++;
            }

            x=0;
            if(hash.count(x))
                ans+=hash[x];
            //花了N长时间终于明白上面这句话什么意思了
            //把?看做空串(也可以理解为删除)和后面的串一块,有多少符合题意的,这时候状态是0

            for(int i=0;i<26;i++)//枚举?的值与后面的串一块,有多少符合题意的
            {
                if(hash.count(x^(1<<i)))
                    ans+=hash[x^(1<<i)];
            }

            for(int i=p-1;i>=0;i--)//处理前部分时,和?以及后半部分建立了联系,注意一定是逆向,不能是正向,
            {
                x^=(1<<(str[i]-'a'));
                if(hash.count(x))//寻找后半部分出现相同的状态,此时?为空, 这也是要逆向的原因
                    ans+=hash[x];
                    
                for(int j=0;j<26;j++)//枚举?的值
                {
                    if(hash.count(x^(1<<j)))
                        ans+=hash[x^(1<<j)];
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

简要介绍下hash.count(x)这个函数,转载于网络

count()是返回key出现的次数,在map里,key只能是0和1

例如 我们判断一个key是否存在,如果存在就输出,不存在就不输出
map<string, int> a;
cout<<a["abc"]<<endl; //这个肯定是错的
if(a.count("China"))        //对了
    cout<<a["China"]<<endl;

map<string,int>::iterator fi=a.find("China");//查找是否有"China",返回一个迭代器
if(fi!=a.end()) cout<<a->second;//找到了,输出"China"对应的int值