day03:字符串&位运算&快速幂
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;
}