西安电子科技大学第16届程序设计竞赛网络同步赛 - Xieldy And His Password (找规律/想法)

西安电子科技大学第16届程序设计竞赛网络同步赛 - Xieldy And His Password (找规律/想法)

题目链接 :Xieldy And His Password


题意

众所周知,Xieldy最常用的口令是**
为了改变这一现状,他random了一个01串,并从中截取了一段作为自己的口令。
他选择的口令满足以下条件:

  1. 口令串表示的二进制数在十进制下可以被表示为3k(k>=0)。
  2. 口令串可以有前导零。

    现已经random出了01串,他想知道有多少种口令方案可以选择(不同的子段即为不同)。

输入描述:

若干组数据,每组数据仅一行01串s,表示random出来的的串,|s|<=1e6

输出描述:

输出口令的方案数。

思路一

​ 既然题目的要求是要我们找一个01串二进制转化为十进制时能够被三整除,那么我们就找一找三的倍数二进制表示有什么规律。然后,我开始了漫漫长夜般探索,很遗憾,我什么都没发现,但是,比完赛后,有同学告诉了我他发现的规律,那就是一个二进制数如果要是三的倍数,那么它的奇数偶数位上的1一定相差3的倍数。

​ 0 11 110 1001 10101 这些例子都满足

那么我们就可以进行构思了,我构造了一个sum数组大小为三,分别记录了前面那一个奇偶差为0,1,2;的组合数。

举个例子

000000 的时候,初始sum[0] = 1,sum[1] = sum[2] = 0;

每次向左移动的过程中,计算当前的奇偶差,如果差为0,访问sum[0],与当前位有关的组合数为前一个奇偶差为0的数加一


代码一

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = (int)(j);i <= (int)(k);i ++)
#define per(i,j,k) for(int i = (int)(j);i >= (int)(k);i --)
#define mmm(a,b)   memset(a,b,sizeof(a))

typedef long long LL;
const int INF = (int)0x3f3f3f3f;
const LL MAXN = (LL)2e6+7;

char str[MAXN];
LL sum[3];
LL odd,eve;
LL ans;

void init(){
    mmm(sum,0);
    sum[0] = 1;
    ans = 0;
    odd = eve = 0;
}

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    while (cin >> str){
        init();
        LL len = strlen(str);
        rep(i,0,len-1){
            if (i&1) odd += str[i]-'0';
            else     eve += str[i]-'0';

            LL cha = ((odd-eve)%3+3)%3;//特别要注意这个操作,不能直接取绝对值,这样会破坏奇偶的差值
            ans += sum[cha];
            sum[cha] += 1;
        }
        cout << ans << endl;
    }
}

思路二

动态规划
01字符串s,长度为n
dp[i][j]:以s[i]结尾,能得到被 3 除余 j 的方案数(0<=i<=n­1,0<=j<=2)
从左往右更新dp数组

  • 若s[i]为‘0’,以s[i]结尾的数字相当于以s[i­1]结尾的数字*2

    3k*2 = 6*k 余0
    (3k+1)*2 = 6*k+2 余2
    (3k+2)*2 = 6*k+4 余1
    再考虑s[i]独自成数字0的情况

    dp[i].[0] = dp[i­-1].[0]+1
    dp[i].[1] = dp[i­-1].[2]
    dp[i].[2] = dp[i­-1].[1]

  • 若s[i]为‘1’,以s[i]结尾的数字相当于以s[i­1]结尾的数字*2+1

    3k*2+1 = 6*k+1 余1
    (3k+1)*2+1 = 6*k+3 余0
    (3k+2)*2+1 = 6*k+5 余2
    再考虑s[i]独自成数字1的情况

    dp[i].[0] = dp[i­-1].[1]
    dp[i].[1] = dp[i­-1].[0]+1
    dp[i].[2] = dp[i­-1].[2]
    ans = Σdp[i].[0]
    时间复杂度O(n)

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = (int)(j);i <= (int)(k);i ++)
#define per(i,j,k) for(int i = (int)(j);i >= (int)(k);i --)
#define mmm(a,b)   memset(a,b,sizeof(a))

typedef long long ll;
const int INF = (int)0x3f3f3f3f;
const int MAXN = (int)1e6+7;

char str[MAXN];
ll dp[MAXN][3];
ll sum;

void init(){
    dp[0][0] = 0;
    dp[0][1] = dp[0][2] = -1;
    sum = 0;
}

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    while (cin >> str+1){
        init();
        ll len = strlen(str+1);
        rep(i,1,len){
            if (str[i] == '0'){
                dp[i][0] = dp[i-1][0]+1;
                dp[i][1] = dp[i-1][2];
                dp[i][2] = dp[i-1][1];
            }else {
                dp[i][0] = dp[i-1][1]+1;
                dp[i][1] = dp[i-1][0];
                dp[i][2] = dp[i-1][2];
            }
            sum += dp[i][0];
        }
        cout << sum << endl;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40513946/article/details/80084407