机试day7
String Matching
String Matching_牛客题霸_牛客网 (nowcoder.com)
描述
Finding all occurrences of a pattern in a text is a problem that arises frequently in text-editing programs. Typically,the text is a document being edited,and the pattern searched for is a particular word supplied by the user. We assume that the text is an array T[1..n] of length n and that the pattern is an array P[1..m] of length m<=n.We further assume that the elements of P and T are all alphabets(∑={a,b...,z}).The character arrays P and T are often called strings of characters. We say that pattern P occurs with shift s in the text T if 0<=s<=n and T[s+1..s+m] = P1..m. If P occurs with shift s in T,then we call s a valid shift;otherwise,we calls a invalid shift. Your task is to calculate the number of vald shifts for the given text T and p attern P.
输入描述:
For each case, there are two strings T and P on a line,separated by a single space.You may assume both the length of T and P will not exceed 10^6.
输出描述:
You should output a number on a separate line,which indicates the number of valid shifts for the given text T and pattern P.
输入:
abababab abab
输出:
3
思路:就是在练习KMP算法编写
#include <iostream>
using namespace std;
int nextTable[1000];
void get_next(string pattern) {
int i = -1, j = 0;
nextTable[0] = -1;
while (j < pattern.size()) {
if (i == -1 || pattern[i] == pattern[j]) {
i++;
j++;
nextTable[j] = i;
} else {
i = nextTable[i];
}
}
}
int KMP(string text, string pattern) {
get_next(pattern);
int number = 0;
int i = 0, j = 0;
while (i < text.size()) {
if (j == -1 || text[i] == pattern[j]) {
i++;
j++;
} else {
j = nextTable[j];
}
if (j == pattern.size()) {
number++;
j = nextTable[j];
}
}
return number;
}
int main() {
string text, pattern;
while (cin >> text >> pattern) {
printf("%d\n", KMP(text, pattern));
}
}
验证IP地址
描述
编写一个函数来验证输入的字符串是否是有效的 IPv4 或 IPv6 地址
IPv4 地址由十进制数和点来表示,每个地址包含4个十进制数,其范围为 0 - 255, 用(".")分割。比如,172.16.254.1;
同时,IPv4 地址内的数不会以 0 开头。比如,地址 172.16.254.01 是不合法的。
IPv6 地址由8组16进制的数字来表示,每组表示 16 比特。这些组数字通过 (":")分割。比如, 2001:0db8:85a3:0000:0000:8a2e:0370:7334 是一个有效的地址。而且,我们可以加入一些以 0 开头的数字,字母可以使用大写,也可以是小写。所以, 2001:db8:85a3:0:0:8A2E:0370:7334 也是一个有效的 IPv6 address地址 (即,忽略 0 开头,忽略大小写)。
然而,我们不能因为某个组的值为 0,而使用一个空的组,以至于出现 (::) 的情况。 比如, 2001:0db8:85a3::8A2E:0370:7334 是无效的 IPv6 地址。
同时,在 IPv6 地址中,多余的 0 也是不被允许的。比如, 02001:0db8:85a3:0000:0000:8a2e:0370:7334 是无效的。
说明: 你可以认为给定的字符串里没有空格或者其他特殊字符。
数据范围:字符串长度满足 $5\le n\le 50$
进阶:空间复杂度$O(n)$,时间复杂度 $O(n)$
法1:超极笨的不停if列举法【没脸看】
略
法2:正则-C++库
透露出一种清澈的愚蠢,谁考试能记得呢:cry:,而且还没有对开头为0的不标准ipv4进行判断
#include <iostream>
#include <regex>
bool is_ipv4(string IP) {
regex pattern("((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)");
smatch res;
if (regex_match(IP, res, pattern))
return true;
else
return false;
}
bool is_ipv6(string IP) {
regex pattern("/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/");
smatch res;
if (regex_match(IP, res, pattern))
return true;
else
return false;
}
class Solution {
public:
/**
* 验证IP地址
* @param IP string字符串 一个IP地址字符串
* @return string字符串
*/
string solve(string IP) {
if (is_ipv4(IP))
return "IPv4";
else if (is_ipv6(IP))
return "IPv6";
else
return "Neither";
}
};
二进制数
描述
大家都知道,数据在计算机里中存储是以二进制的形式存储的。 有一天,小明学了C语言之后,他想知道一个类型为unsigned int 类型的数字,存储在计算机中的二进制串是什么样子的。 你能帮帮小明吗?并且,小明不想要二进制串中前面的没有意义的0串,即要去掉前导0。
输入描述:
多行,每一行表示要求的数字
输出描述:
输出共T行。每行输出求得的二进制串。
思路:vector之类的数据结构接收每次模2的结果,最后倒着输出
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
while (cin >> n) {
vector<int> binary;
while (n != 0) {
binary.push_back(n % 2);
n /= 2;
}
for (int i = binary.size() - 1; i >= 0; i--) {
cout << binary[i];
}
cout << endl;
}
}
进制转换
描述
将一个长度最多为30位数字的十进制非负整数转换为二进制数输出。
输入描述:
多组数据,每行为一个长度不超过30位的十进制非负整数。 (注意是10进制数字的个数可能有30个,而非30bits的整数)
输出描述:
每行输出对应的二进制数。
思路:由于长度太长,肯定得用字符串来处理;模2余数好算,看最后一位即可;难点在于除以2后的结果
单独写了个chu2函数,从第一位开始遍历算起,如果除以2有余数,则下一位除以2前+10。特殊情况是第一位为1(1除外)的情况,需要最后去除第一位
#include <iostream>
#include <vector>
using namespace std;
string chu2(string n) {
int num, yu = 0, flag = 0;
for (int i = 0; i < n.size(); i++) {
num = n[i] - '0';
if (n[i] == '1' && i == 0 && n.size() != 1)
flag = 1;
else
n[i] = (yu * 10 + num) / 2 + '0';
yu = num % 2;
}
if (flag == 1)
n.erase(0, 1);
return n;
}
int main() {
string n;
while (cin >> n) {
vector<int> binary;
if (n == "0")
binary.push_back(0);
else {
while (n != "0") {
binary.push_back((n[n.size() - 1] - '0') % 2);
n = chu2(n);
}
}
for (int i = binary.size() - 1; i >= 0; i--) {
cout << binary[i];
}
cout << endl;
}
}
10进制 VS 2进制
10进制 VS 2进制_牛客题霸_牛客网 (nowcoder.com)
描述
对于一个十进制数A,将A转换为二进制数,然后按位逆序排列,再转换为十进制数B,我们称B为A的二进制逆序数。 例如对于十进制数173,它的二进制形式为10101101,逆序排列得到10110101,其十进制数为181,181即为173的二进制逆序数。
输入描述:
一个1000位(即10^999)以内的十进制数。
输出描述:
输入的十进制数的二进制逆序数。
输入:
173
输出:
181
思路:这题很难,对于十进制转二进制就上一题的方法,但是二进制也要转换为十进制,需要再写个字符串乘法和加法函数
直接照抄书上的了,模板题【做的脑CPU都快烧了】
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
string Divide(string str, int x) {
int remainder = 0;
for (int i = 0; i < str.size(); i++) {
int current = remainder * 10 + str[i] - '0';
str[i] = current / x + '0';
remainder = current % x;
}
int pos = 0;
while (str[pos] == '0')
pos++;
return str.substr(pos);
}
string Multiple(string str, int x) {
int carry = 0;
for (int i = str.size() - 1; i >= 0; i--) {
int current = x * (str[i] - '0') + carry;
str[i] = current % 10 + '0';
carry = current / 10;
}
if (carry != 0)
str = "1" + str;
return str;
}
string Add(string str, int x) {
int carry = x;
for (int i = str.size() - 1; i >= 0; i--) {
int current = (str[i] - '0') + carry;
str[i] = current % 10 + '0';
carry = current / 10;
}
if (carry != 0)
str = "1" + str;
return str;
}
int main() {
string str;
while (cin >> str) {
vector<int> binary;
while (str.size() != 0) {
int last = str[str.size() - 1] - '0';
binary.push_back(last % 2);
str = Divide(str, 2);
}
string answer = "0";
for (int i = 0; i < binary.size(); i++) {
answer = Multiple(answer, 2);
answer = Add(answer, binary[i]);
}
cout << answer << endl;
}
return 0;
}
进制转换2
描述
将M进制的数X转换为N进制的数输出。
输入描述:
输入的第一行包括两个整数:M和N(2<=M,N<=36)。
下面的一行输入一个数X,X是M进制的数,现在要求你将M进制的数X转换成N进制的数输出。
输出描述:
输出X的N进制表示的数。
输入:
10 2
11
输出:
1011
【注意输入时如有字母,则字母为大写,输出时如有字母,则字母为小写。】
思路:先转为10进制再转为N进制,要考虑大于10进制的字母形式【难点】
需要注意的是int溢出!!!所以用long long
#include <iostream>
#include <vector>
using namespace std;
int ch2int(char c) {
if (c <= '9' && c >= '0')
return c - '0';
else
return c - 'A' + 10;
}
char int2ch(int x) {
if (x < 10)
return char(x + '0');
else {
return char(x - 10 + 'a');
}
}
int main() {
int M, N;
cin >> M >> N;
string x;
cin >> x;
long long dec = 0;
for (int i = 0; i < x.size(); i++) {
dec = M * dec + ch2int(x[i]);
}
vector<char> n_num;
while (dec != 0) {
n_num.push_back(int2ch(dec % N));
dec /= N;
}
for (int i = n_num.size() - 1; i >= 0; i--) {
cout << n_num[i];
}
cout << endl;
}
八进制
输入一个整数转为八进制,做了前面的题目此题1min就敲完了
#include <iostream>
#include <vector>
using namespace std;
int main() {
int N;
while (cin >> N) {
vector<int> oct;
while (N != 0) {
oct.push_back(N % 8);
N /= 8;
}
for (int i = oct.size() - 1; i >= 0; i--) {
cout << oct[i];
}
cout << endl;
}
}
又一版 A+B
又一版 A+B_牛客题霸_牛客网 (nowcoder.com)
描述
输入两个不超过整型定义的非负10进制整数A和B($\le 2^{31}-1$),输出A+B的m ($1\lt m\lt 10$)进制数。
输入描述:
输入格式:测试输入包含若干测试用例。每个测试用例占一行,给出m和A,B的值。 当m为0时输入结束。
输出描述:
输出格式:每个测试用例的输出占一行,输出A+B的m进制数。
法1:字符串处理
思路:如果A+B不溢出,那就很好做,直接按照正常数字进制转换即可;但是测试案例里有A+B很大溢出的情况,因此需要多加考虑
我的想法是既然十进制加起来可能会溢出,那就先不相加,先转为对应的m进制后再相加,此时就好做多了;我是使用的字符串形式来计算,看起来有些麻烦;此种方法的好处是不用再去写转换字符串进制转换了(除法)
#include <iostream>
#include <algorithm>
using namespace std;
string Add(string a, string b, int m) {
int carry = 0;
if (a.size() > b.size()) { // 判断哪个长就用哪个接收最终相加结果
int current = 0;
for (int i = 0; i < a.size(); i++) {
if (i < b.size()) {
current = (a[i] - '0') + (b[i] - '0') + carry;
a[i] = current % m + '0';
carry = current / m;
}
else { // 超过短的长度后,不用取值,否则数组溢出
current = (a[i] - '0') + carry;
a[i] = current % m + '0';
carry = current / m;
}
}
if (carry != 0) // 最后还要在判断下,防止进位超出最大长度
a.push_back(carry + '0');
return a;
} else {
int current = 0;
for (int i = 0; i < b.size(); i++) {
if (i < a.size()) {
current = (b[i] - '0') + (a[i] - '0') + carry;
b[i] = current % m + '0';
carry = current / m;
}
else {
current = (b[i] - '0') + carry;
b[i] = current % m + '0';
carry = current / m;
}
}
if (carry != 0)
b.push_back(carry + '0');
return b;
}
}
int main() {
int m, a, b;
while (cin >> m >> a >> b) {
if (m == 0)
break;
string oct_a;
while (a != 0) {
oct_a.push_back(a % m + '0'); // 每次添加一个字符
a /= m;
}
string oct_b;
while (b != 0) {
oct_b.push_back(b % m + '0');
b /= m;
}
if (oct_a.size() == 0 && oct_b.size() == 0) //如果a、b都是0,直接输出0
cout << 0 << endl;
else {
string result = Add(oct_a, oct_b, m); // 字符串结果还要逆向一下才是正确的顺序
reverse(result.begin(), result.end());
cout << result << endl;
}
}
}
改进:
- 数字进制转换写成了函数,这样增加代码可读性
- 字符串相加函数中,使用while给较短字符串补0(参考其他人题解),使得长度相等,这样不用重复两遍代码了
#include <iostream>
#include <algorithm>
using namespace std;
string Add(string a, string b, int m) {
while (a.size() > b.size())
b.push_back('0');
while (a.size() < b.size())
a.push_back('0');
int carry = 0;
int current = 0;
for (int i = 0; i < a.size(); i++) {
current = (a[i] - '0') + (b[i] - '0') + carry;
a[i] = current % m + '0';
carry = current / m;
}
if (carry != 0)
a.push_back(carry + '0');
return a;
}
string dec2m(int x, int m) {
string s;
while (x != 0) {
s.push_back(x % m + '0');
x /= m;
}
return s;
}
int main() {
int m, a, b;
while (cin >> m >> a >> b) {
if (m == 0)
break;
string oct_a = dec2m(a, m);
string oct_b = dec2m(b, m);
if (oct_a.size() == 0 && oct_b.size() == 0)
cout << 0 << endl;
else {
string result = Add(oct_a, oct_b, m);
reverse(result.begin(), result.end());
cout << result << endl;
}
}
}
法2:直接long long接收A、B(感觉有些作弊?)
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int m;
long long A, B;
while (cin >> m) {
if (m == 0) break;
cin >> A;
cin >> B;
long long val = A + B;
vector<int> res;
if (val == 0) res.push_back(0);
while (val != 0) {
res.push_back(val % m);
val = val / m;
}
for (int i = res.size()-1; i >= 0; i--) {
cout << res[i];
}
cout << endl;
}
return 0;
}
进制转换
描述
写出一个程序,接受一个十六进制的数值字符串,输出该数值的十进制字符串(注意可能存在的一个测试用例里的多组数据)。
输入描述:
输入一个十六进制的数值字符串。
输出描述:
输出该数值的十进制字符串。
法1:16进制字符转10进制
#include <iostream>
using namespace std;
int hex2dec(char ch) {
if (ch <= '9' && ch >= '0')
return ch - '0';
else
return ch - 'A' + 10;
}
int main() {
string hex;
while (cin >> hex) {
int dec = 0;
for (int i = 2; i < hex.size(); i++) {
dec = dec * 16 + hex2dec(hex[i]);
}
cout << dec << endl;
}
}
法2:超级简单,直接格式化输入和输出
C的写法:
#include <stdio.h>
int main() {
int num = 0;
while (~scanf ("%x", &num))
printf("%d\n", num);
return 0;
}
C++的写法:
#include <iostream>
using namespace std;
int main() {
int num = 0;
while (cin >> hex >> num)
cout << dec << num << endl;
return 0;
}
数制转换
描述
求任意两个不同进制非负整数的转换(2进制~16进制),所给整数在long所能表达的范围之内。 不同进制的表示符号为(0,1,...,9,a,b,...,f)或者(0,1,...,9,A,B,...,F)。
输入描述:
输入只有一行,包含三个整数a,n,b。a表示其后的n 是a进制整数,b表示欲将a进制整数n转换成b进制整数。a,b是十进制整数,2 =< a,b <= 16。 数据可能存在包含前导零的情况。
输出描述:
可能有多组测试数据,对于每组数据,输出包含一行,该行有一个整数为转换后的b进制数。输出时字母符号全部用大写表示,即(0,1,...,9,A,B,...,F)。
思路:
- 首先a进制字符串转为十进制数字(a2dec),其中设置flag位为0,直到遇到开头不为'0'的字符,即去除开头的0
- 十进制数字转为b进制字符串(dec2b),需要注意dec % b如果大于等于10,要改为字母,因此做一个条件判断
- 如果b=10,第一步完可以直接输出,不用再去dec2b里转为字符串了
#include <iostream>
#include <algorithm>
using namespace std;
int a2dec(string num, int a) {
int dec = 0, n, flag = 0;
for (int i = 0; i < num.size(); i++) {
if (num[i] != '0')
flag = 1;
if (flag) {
if (num[i] <= '9' && num[i] >= '0')
n = num[i] - '0';
else if (num[i] <= 'z' && num[i] >= 'a')
n = num[i] - 'a' + 10;
else
n = num[i] - 'A' + 10;
dec = dec * a + n;
}
}
return dec;
}
string dec2b(int dec, int b) {
string num;
while (dec != 0) {
if (dec % b >= 10)
num.push_back(dec % b + 'A' - 10);
else
num.push_back(dec % b + '0');
dec /= b;
}
reverse(num.begin(), num.end()); //得逆转下顺序
return num;
}
int main() {
int a, b;
string num;
while (cin >> a >> num >> b) {
int dec = a2dec(num, a);
if (b != 10)
cout << dec2b(dec, b) << endl;
else
cout << dec << endl;
}
}