目录

  • day03:字符串&位运算&快速幂
  • 1. P5015 [NOIP2018 普及组] 标题统计
  • 2. P1200 [USACO1.1]你的飞碟在这儿Your Ride Is Here
  • 3. P1125 [NOIP2008 提高组] 笨小猴
  • 4. P1226 【模板】快速幂||取余运算

day03:字符串&位运算&快速幂

字符串
什么是字符,什么有又是字符串?

字符一般是指单个字符,用单引号括起来的字符
字符串是指多个字符的集合,用双引号括起来

一般我们存储字符串有两种方式:
一个是字符数组char s[N], char s[N][N]; 一个是字符串类型:string s;

字符数组的输入输出:

int main(){
    const int N=10; char a[N];
    for(int i=0; i<N; i++){
        a[i] = getchar();//scanf("%c", &a[i]);//'\n'也会读入
    }
    for(int i=0; i<N; i++){
        putchar(a[i]); //printf("%c", a[i]);
    }
    return 0;
}

或者这样输入输出

gets(a); //scanf("%s", &a);
puts(a); //printf("%s", a);

但是string类型的输入输出就方便多了

string s; 
cin>>s;//string类型的输入
cout<<s;//string类型的输出
for(int i=0; i<s.length(); i++){
    cout<<s[i];//string类型的输出
}

C语言中字符串封装的一些函数方法

//比较两个字符串的内容是否相等
int strcmp(const char str1,const char str2);

//比较两个字符串前n个字符是否完全一致
int strncmp(const char str1,const char str2,size_t n);

//查找指定字符在指定字符串中第一次出现的位置
char* strchr(const char* str, char c);

//查找指定字符在指定字符串中最后一次出现的位置
char* strrchr(const char*str, char c);

//在字符串中搜索是否包含一个子字符串
char* strstr(const charhaystack, const char needle);

//将指针src指向的字符串连接到指针dest指向的字符串后面
char* strcat(char* dest, const char* src);

//从src指向的字符串中取不超过n个字符连接到dest指向的字符串后面
char* strncat(char* dest, const char* src,size_t n);

//字符串指针src所指向的字符串将被复制到dest所指向的字符串中。
char* strcpy(char* dest, const char* src);

//取字符串长度函数,不含'\0'
int strlen(char* str);

//大小写转换
strupr(char* str);
strlwr(char* str);

string类的一些方法
append() 	– 在字符串的末尾添加字符
find() 		– 在字符串中查找字符串
insert()	– 插入字符
length() 	– 返回字符串的长度
replace() 	– 替换字符串
substr() 	– 返回某个子字符串

#include<iostream>
using namespace std;
int main() {
    string s="12345678901234567890"; 
    cout<<s<<endl;//12345678901234567890

    s.append("a");
    cout<<s<<endl;//12345678901234567890a
    cout<<s.find("0")<<endl;// 9

    s.insert(0, "b");
    cout<<s<<endl;//b12345678901234567890a
    cout<<s.length()<<endl;//22

    s.replace(0, 2, "a");
    cout<<s<<endl;//a2345678901234567890a
    cout<<s.substr(0,10)<<endl;//a234567890
    return 0;
}

位运算
学习位运算前,需要掌握二进制,那么我们这里就将进制转换在复习一下

二进制:1001B  = 8+1 =9D
八进制:1005O  = 1*8^3+5*8^0=8^3+5
十进制:1289D  = 1289D
十六进制:0x119f = 1*16^3+1*16^2+9*16^1 +15*16^0

计算公式:结果 = sum(基数*位数权重)
&: 按位与,同真为真,其余为假
|: 按位或,同假为假,其余为真
^: 异或运算,相异为真,相同为假
~: 取反,真假颠倒
<<: 左移:按二进制把数字向左移动对应位数,高位移出(舍弃),低位的空位补0。
>>: 右移:按二进制把数字向右移动对应位数,低位移出(舍弃),高位的空位补符号位,即正数补0,负数补1
>>左移运算符:num << n; 相当于num乘以2的n次方(低位补0)
>>左移运算符:num >> n; 相当于num除以2的n次方
10D = 8+2 --> 1010B<<2 = 101000B = 32+8=40 = 10*2^2
10D = 8+2 --> 1010B>>2 = 0010B = 2 = 10/(2^2)=2

原码,反码,补码是机器存储一个具体数字的编码方式
原码就是符号位加上真值的绝对值,即用第一位表示符号,其余位表示值

比如如果是8位二进制:
+2的原码 = 0000 0010
-2的原码 = 1000 0010
第一位为符号位,所以8位二进制取值范围为[1111 1111, 0111 1111] 也就是[-127, 127]

反码:正数的反码就是原码,负数的反码是在原码的基础上,符号位不变,其余位取反

+2的反码 = 0000 0010
-2的反码 = 1111 1101

补码:正数的补码就是原码,负数的补码是在反码的基础上+1

+2的反码 = 0000 0010
-2的反码 = 1111 1110
总结:
    正数的原码,反码,补码都是原数的二进制; 
    负数的原码就是符号位为1,其余位按正数计算
    负数的反码就是符号位为1,其余位按正数取反
    负数的补码就是符号位为1,其余位按正数取反后+1

原码:10D=0000 1010B, -10D=1000 1010B
反码:10D=0000 1010B, -10D=1111 0101B
补码:10D=0000 1010B, -10D=1111 0110B

快速幂
我们都会使用函数:pow(a, n) = a^n,也就是n个a的成绩

于是很容易模拟出来

#include<bits/stdc++.h>
using namespace std;
long long pow(int a, int n){
    long long ans=1;
    for(int i=1; i<=n; i++){
        ans *= a;
    }
    return ans;
}
int main(){
    cout<<pow(2,10);
    return 0;
}

可以看出来,上述求幂运算的时间复杂度为O(n)
我们发现其中对结果求乘积的步骤只有当该位基数为1才进行,所以可以使用位运算来改进这个算法
快速幂实现

#include<bits/stdc++.h>
using namespace std;
long long fastPow(int a, int n){
    long long ans=1;
    while(n){
        if(n&1) ans *= a;
        a *= a;
        n>>=1;//右移1位
    }
    return ans;
}
int main(){
    cout<<fastPow(2,10);
    return 0;
}

注意:快速幂的运算结果是呈几何倍数的增加,所以很容易爆范围,故而开long long
经常对于这样的题目,会说对某个数取模,这里我们可以记住以下几个公式

(a + b) % p = (a % p + b % p) % p
(a - b) % p = (a % p - b % p) % p
(a * b) % p = (a % p * b % p) % p

如果要求取模mod,可以对fastPow函数进行修改

long long fastPow(int a, int n){
    long long ans=1;
    while(n){
    if(n&1) ans = ans*a%mod;//取模
        a = a*a%mod;//取模
        n>>=1; //右移1位
    }
    return ans;
}

1. P5015 [NOIP2018 普及组] 标题统计

【题目描述】凯凯刚写了一篇美妙的作文,请问这篇作文的标题中有多少个字符? 注意:标题中可能包含大、小写英文字母、数字字符、空格和换行符。统计标题字 符数时,空格和换行符不计算在内。

输入格式:输入文件只有一行,一个字符串 s。

输出格式:输出文件只有一行,包含一个整数,即作文标题的字符数(不含空格和换行符)。

输入样例:234 输出样例:3

输入样例:Ca 45 输出样例:4

题解:

#include<bits/stdc++.h>
using namespace std;
int main(){
    string str; getline(cin, str);//读入一行,赋值给str,结束符为换行
    int ans=0;
    for(int i=0; i<str.size(); i++){
        if(s[i]<='9' && s[i]>='0'){
            ans++;
        } else if(s[i]<='z' && s[i]>='a'){
            ans++;
        }else if(s[i]<='Z' && s[i]>='A'){
            ans++;
        }
    }
    cout<<ans; return 0;
}

2. P1200 [USACO1.1]你的飞碟在这儿Your Ride Is Here

【题目描述】众所周知,在每一个彗星后都有一只UFO。这些UFO时常来收集地球上的忠诚支持者。不幸的是,他们的飞碟每次出行都只能带上一组支持者。因此,他们要用一种聪明的方案让这些小组提前知道谁会被彗星带走。他们为每个彗星起了一个名字,通过这些名字来决定这个小组是不是被带走的那个特定的小组(你认为是谁给这些彗星取的名字呢?)。关于如何搭配的细节会在下面告诉你;你的任务是写一个程序,通过小组名和彗星名来决定这个小组是否能被那颗彗星后面的UFO带走。

小组名和彗星名都以下列方式转换成一个数字:最终的数字就是名字中所有字母的积,其中A是1,Z是26。例如,USACO小组就是21×19×1×3×15=17955。如果小组的数字mod47等于彗星的数字mod47,你就得告诉这个小组需要准备好被带走!(记住“a mod b”是a除以b的余数;34mod10等于4)

写出一个程序,读入彗星名和小组名并算出用上面的方案能否将两个名字搭配起来,如果能搭配,就输出“GO”,否则输出“STAY”。小组名和彗星名均是没有空格或标点的一串大写字母(不超过6个字母)。

输入格式:

第1行:一个长度为11到66的大写字母串,表示彗星的名字。
第2行:一个长度为11到66的大写字母串,表示队伍的名字。

输出格式:无

输入样例:

COMETQ
HVNGAT

输出样例:

GO

题解:

#include<iostream>
using namespace std;
int main(){
    string s1,s2;    cin>>s1>>s2;
    int ans1=1,ans2=1;
    for(int i=0; i<s1.size(); i++)	ans1 *= (s1[i]-'A')+1;
    for(int i=0; i<s2.size(); i++)	ans2 *= (s2[i]-'A')+1;
    ans1 %= 47;    ans2 %= 47;
    if(ans1==ans2)    cout<<"GO";
    else    cout<<"STAY";
    return 0;
}

3. P1125 [NOIP2008 提高组] 笨小猴

【题目描述】笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼。但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大!

这种方法的具体描述如下:假设maxn是单词中出现次数最多的字母的出现次数,minn是单词中出现次数最少的字母的出现次数,如果maxn-minn是一个质数,那么笨小猴就认为这是个Lucky Word,这样的单词很可能就是正确的答案。

输入格式:一个单词,其中只可能出现小写字母,并且长度小于100。

输出格式:共两行,第一行是一个字符串,假设输入的的单词是Lucky Word,那么输出“Lucky Word”,否则输出“No Answer”;

第二行是一个整数,如果输入单词是Lucky Word,输出maxn-minn的值,否则输出00。

输入样例:error

输出样例:

Lucky Word
2

输入样例:olympic

输出样例:

No Answer
0

题解:

#include<bits/stdc++.h>
using namespace std;
const int N=27;
int a[N];
bool isPrime(int n){//质数判断函数 
    if(n<2) return false;
    for(int i=2; i*i<=n; i++){
        if(n%i==0) return false;
    }
    return true;
}

int main(){
    string str;    cin>>str;
    int maxn=0,minn=0;
    for(int i=0; i<str.length(); i++){
         a[str[i]-'a'+1]++; //桶排序的知识
    }
    //a[1]~a[26]  这个对应的就是'a'~'z'出现的次数 
    sort(a+1, a+1+26);//升序排序 
    //a[i] = {0, 0, 0, 1, 2, 3}
    maxn=a[26]; 
    for(int i=1; i<=26; i++){
        if(a[i]!=0){
            minn=a[i];break;//退出当前循环 
        }
    } 
    int num=maxn-minn;
    bool flag = isPrime(num);  //判断num是不是质数
    if(flag){
        cout<<"Lucky Word"<<endl<<num;
    } else{
        cout<<"No Answer"<<endl<<0; 
    }return 0;
}

4. P1226 【模板】快速幂||取余运算

【题目描述】给你三个整数 a,b,p,求 a^b mod p。

输入格式:输入只有一行三个整数,分别代表 a,b,p。

输出格式:输出一行一个字符串 a^b mod p=s,其中 a,b,p分别为题目给定的值,s 为运算结果。

输入样例:2 10 9

输出样例:2^10 mod 9=7

数据范围:对于100% 的数据,保证 0≤a, b<2^31,a+b>0,2≤p<231

题解:

#include<bits/stdc++.h> 
using namespace std;
long long fastpow(long long a, long long n, long long p){
    long long ans=1;
    while(n){
        if(n&1) ans = ans*a%p; 
        a = a*a%p;
        n>>=1;
    }
    return ans;
}
int main(){
    long long a,b,p; 	cin>>a>>b>>p;
    long long s = fastpow(a,b,p)%p;
    cout<<a<<"^"<<b<<" mod "<<p<<"="<<s;
    return 0;
}