爱思创 180912 AK串 II 题解

180912 AK串 II

题目思路

题目大意

给定整数 \(n\)\(m\)\(n\) 表示AK串的位数,\(m\) 表示所有 \(n\) 位AK串中不允许出现的前缀,求出合法的 \(n\) 位AK串的个数。(AK串指只由字符A和K组成的固定位数的字符串)

我们用枚举法来做这一道题,用状态压缩来常数优化

我们把题目分成两部分,输入部分和处理部分

输入部分

先定义两个数组,一个存前缀(不存字符串,存整数,二进制代表字符串),另一个存前缀的长度。
每次输入一个字符串,就要把字符串转化成整数
建一个函数int trans(string)
里面我们把 'A' 设为1,'K'设为0
每次乘2在加1或0
然后把得到的数的二进制的后面补0直到 \(n\)

int trans(string k)
{
    int x = 0;
    for(int i = 0; i < k.size(); i ++)
    {
        if(k[i] == 'A')
        {
            x <<= 1;//左移1位比乘2更快
            x += 1;
        }
        else x <<= 1;
    }
    x <<= (n - k.size());//左移补0
    return x;
}

处理部分

先写一个框架

for(int i = 0; i < (1 << n); i ++)//枚举一切长度为n的AK串
{
    if(check(i))//重点:判断前缀
    {
        ans ++;
    }
}

然后写check()函数
把每一个前缀枚举一遍,用&来取前缀,再与非法前缀比较

bool check(int k)
{
    for(int i = 1; i <= m; i ++)
    {
        int t = (1 << len[i]) - 1;//最后len[i]位为1
        t <<= (n - len[i]);//t左移
        int y = t & k;//前缀
        if((y ^ ffc[i]) == 0) return false;//相同
    }
    return true;
}

完整代码

#include<bits/stdc++.h>
using namespace std;
int ffc[20], len[20], n, m;
int trans(string k)
{
    int x = 0;
    for(int i = 0; i < k.size(); i ++)
    {
        if(k[i] == 'A')
        {
            x <<= 1;
            x += 1;
        }
        else x <<= 1;
    }
    x <<= (n - k.size());
    return x;
}
bool check(int k)
{
    for(int i = 1; i <= m; i ++)
    {
        int t = (1 << len[i]) - 1;
        t <<= (n - len[i]);
        int y = t & k;
        if((y ^ ffc[i]) == 0) return false;
    }
    return true;
}
int main()
{
    int ans = 0;
    cin >> n >> m;
    string s;
    for(int i = 1; i <= m; i ++)
    {
        cin >> s;
        ffc[i] = trans(s);
        len[i] = s.size();
    }
    for(int i = 0; i < (1 << n); i ++)
    {
        if(check(i))
        {
            ans ++;
        }
    }
    cout << ans;
    return 0;
}
posted @ 2022-11-26 15:37  yuzihang  阅读(78)  评论(0)    收藏  举报