趣玩法(1)---字符col编码定义

知识又双叒叕陷入了瓶颈期,所以今天咱们来玩点新鲜的。

1.字符串col编码定义

在牵扯到char类型的算法当中,半数牵扯到ASCII。我们都知道,ASCII字符集中的字符用数字编码表示(当然在C++实现中使用其主机系统的编码)。例如,字母A的编码为65,字母Z的编码是90。那么Rick的编码呢?

很显然,这个Rick指代不明。如果带上双引号写成"Rick"这样,那是没有编码的。因为此时"Rick"成为一个string类(或其他)的字符串,常理来说,字符串不具有编码属性。但这个Rick若成为一个变量名,这还有的商量。

那么如何让字符串显式地带有编码属性呢?咱们可以人为定义一个,就叫他集体编码(Colcode)好了。比如,若使一个string类字符串的编码值,等于字符串当中每个字符的ASCII值之和。即

s∈string且strlen(s)==n;则定义Colcode(s)=Σ(i=0,n-1)ASCII(s[i])

(看不懂没关系)

2.(例1)col码汇总

所以请问,一个字符串如何知道他的col码呢?我们用代码可以实现。

普通版

#include<bits/stdc++.h>
using namespace std;
int main()
{
    unsigned long long cnt=0;
    string s;
    getline(cin,s);
    for(int i=0;i<s.size();i++)
        cnt+=int(s[i]);
    cout<<cnt<<endl;
}

运行可以得到: 

但这个程序的缺陷就是,如果遇到超长的字符串(远比你想象的长),那unsigned long long都无法容下(属于极端情况)。下面是

高精度版

#include<bits/stdc++.h>
using namespace std;
struct bigint
{
    int len,a[1000];
    bigint(int x=0)
    {
        memset(a,0,sizeof a);
        for(len=1;x;len++)
            a[len]=x%10,x/=10;
        len--;
    }
    int &operator[](int i)
    {
        return a[i];
    }
    void flatten(int lt)
    {
        len=lt;
        for(int i=1;i<=len;i++)
            a[i+1]+=a[i]/10,a[i]%=10;
        while(!a[len])
            len--;
    }
    void print()
    {
        for(int i=max(len,1);i>=1;i--)
            cout<<a[i];
    }
};
bigint operator+(bigint a,bigint b)
{
    bigint c;
    int len=max(a.len,b.len);
    for(int i=1;i<=len;i++)
        c[i]+=a[i]+b[i];
    c.flatten(len+1);
    return c;
}
int main()
{
    bigint cnt(0);
    string s;
    getline(cin,s);
    for(int i=0;i<s.size();i++)
        cnt=cnt+int(s[i]);
    cnt.print();
}

3.(例2)col码列举

那么请问,如果已知一个大写字母字符串的col编码,那字符串有哪些可能呢?先来看一个

简化版的问题

已知一个字符串由两个大写字母组成,其col编码值为155,那这个字符串可能是什么呢?

或许可以用两层循环实现。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    cin>>n;
    for(int i=65;i<=90;i++)
        for(int j=65;j<=90;j++)
            if(i+j==n)  cout<<char(i)<<" "<<char(j)<<endl;
}

那如果原题是三个大写字母呢?(三层循环)

如果一百个大写字母呢?(……一百层循环)

如果两兆个呢?(……慢慢敲)

这还不是硬伤。

硬伤是,如果不给出这个字符串长度,要求直接列出所有的可能呢?

有的人用分支语句。毕竟:只要头够铁,多少循环都能干出来、敲出来。

但有的人想要一种更省键盘的方法。

咱们需要用到搜索(一种算法)。具体可以去看《洛谷》的第14章或oi-wiki网站。

那么本题需要用到DFS(Depth-First-Search,深度优先搜索)算法。

一道例题

int m, arr[103];  // arr 用于记录方案

void dfs(int n, int i, int a) 
{
  if (n == 0) 
  {
    for (int j = 1; j <= i - 1; ++j) printf("%d ", arr[j]);
    printf("\n");
  }
  if (i <= m) 
  {
    for (int j = a; j <= n; ++j) 
    {
      arr[i] = j;
      dfs(n - j, i + 1, j);  // 请仔细思考该行含义。
    }
  }
}

// 主函数
scanf("%d%d", &n, &m);
dfs(n, 1, 1);

imp:该范例的思路是,已知一个总和,先在第一个空填数,然后将总和减去这个数,然后再在第二个空填数,然后继续递减。当总和只剩0时,说明列举完成,进行整理。这很明显是操作相似但规模缩小的算法。

那么,不知道大家是否发现,这个搜索的逻辑细节和咱们手写这种题是不一样的。

比如用手写6=x+y+z(x, y, z∈N+ 且 x<y<z),我们肯定会令第一个数为1。

那么就成了6=1+y+z。

那么继续填y,又因为y≥x,就成了6=1+1+z。

那么这个时候,大家会填什么呢?

我会填z=4。

但是此程序中,仍然填的是z=1

回看程序在main()函数中的首次调用。

dfs(n, 1, 1);

很明显,第一次调用中a=1,所以开始有j=1。

然后二次递归,新的n=5,新的a=1,继续进行迭代,一直到最后,每个数只要满足比前面大即可,所以三个数都填了1。

那么问题来了,6不可能等于1+1+1,那怎么办呢?

将第3个数填完以后,新的n=3,新的i=4。

新调用中,“n==0”和“i<=m”两个分支都不满足。所以此次调用只是走了一遍,他不进行任何操作。

那么回到上一层递归,由于递归返回,所以循环继续迭代,此时成了1+1+2。

当然还不符合要求,所以继续一个一个试。

所以很明显,此种搜索算法只是省了键盘,但是思路没有变化,他和循环方法的思路一样略为繁杂,这也符合这种算法的基本特点。

其次,该范例有问题。运行可以看出来。

原提要求裁成m个正整数,但运行后发现参差不齐。什么原因呢?

类比imp处的思路,有3个数字格,要求和为6。请注意这一行。

for (int j = a; j <= n; ++j) ;

第一次传进去的参数是1,那么a初始等于1,但是n=6,所以a迭代必然会迭代到6。

那么arr[i]为6,又因为i=1,所以arr[i]=6,故n-j=0,进行二次递归。

此时n-j成为了新的n,新的n=0,符合第一个分支(n==0),所以进行了输出。

那我们如何解决这个问题呢?

请大家回看代码,参数"i"是什么?

答:i是待填入数的指针

那么i-1是?答:已填入数的数量

这不就完了?

只需在n==0后面补加一个条件:i-1==m,意思是保证已填入数的数量(i-1)与要求的数量(m)相等。

这不就好了?

上完整代码

#include<iostream>
using namespace std;
int m, arr[103];  // arr 用于记录方案
int n;
void dfs(int n, int i, int a)//n为除掉前i-1个数外剩余数的总和,n为坐标迭代器,a为迭代的开始
{
  	if (n == 0 && i - 1 == m) //剩余数和已经等于0,且已经填满
  	{
    	for (int j = 1; j <= i - 1; ++j) printf("%d ", arr[j]);
    	printf("\n");
	}
  	if (i <= m) 
  	{
    	for (int j = a; j <= n; ++j) 
    	{
      		arr[i] = j;
      		dfs(n - j, i + 1, j);
    	}
  	}
}
int main()
{
	scanf("%d%d", &n, &m);//可以换成 cin>>n>>m;
	dfs(n, 1, 1);
}

原题

大家也可以去看洛谷U408212(出错了 - 洛谷)题。

那么对于“已知一个大写字母字符串的col编码,字符串有哪些可能”这样的问题,如何解决呢?

1.上面这道题有数列单调不减的要求,但是这个没有;上一篇代码中的dfs()函数中,参数a就是用来满足这个要求的,所以完全可以删除这个参数。

2.        for (int j = a; j <= n; ++j)

将这句改成从65(‘A’的ASCII值)到90(‘Z’的ASCII值)的迭代。

3.去除我刚才添加的i-1==m这个条件。并在后面加上这个。

for(int k=1;k*65<=s;k++)
    	if(k*90<s)	continue;
    	else 
    	{
    		m=k;
    		dfs(s, 1);
    	}

请大家思考一下这几句的含义。当然,这几句的作用,用其他的方法也可以代替(请大家自行尝试)。

类比这个思路,请问,如果已知一个小写字母字符串的col编码,那字符串有哪些可能呢?

大家可以去做洛谷U408427《又见Col编码》题。

欢迎讨论区交流。

  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值