Loading... # 机试day7 ## String Matching [String Matching_牛客题霸_牛客网 (nowcoder.com)](https://www.nowcoder.com/practice/00438b0bc9384ceeb65613346b42e88a?tpId=62&tqId=29448&tPage=1&ru=/kaoyan/retest/2002&qru=/ta/sju-kaoyan/question-ranking) **描述** 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] = P[1..m](that is if T[s+j]=P[j],for 1<=j<=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算法编写 ~~~c++ #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进行判断 ~~~c++ #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"; } }; ~~~ ## 二进制数 [二进制数_牛客题霸_牛客网 (nowcoder.com)](https://www.nowcoder.com/practice/103dd589fed14457a673c613d8de3841?tpId=67&tqId=29634&tPage=1&ru=/kaoyan/retest/1005&qru=/ta/bupt-kaoyan/question-ranking) **描述** 大家都知道,数据在计算机里中存储是以二进制的形式存储的。 有一天,小明学了C语言之后,他想知道一个类型为unsigned int 类型的数字,存储在计算机中的二进制串是什么样子的。 你能帮帮小明吗?并且,小明不想要二进制串中前面的没有意义的0串,即要去掉前导0。 **输入描述**: 多行,每一行表示要求的数字 **输出描述**: 输出共T行。每行输出求得的二进制串。 思路:vector之类的数据结构接收每次模2的结果,最后倒着输出 ~~~c++ #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; } } ~~~ ## 进制转换 [进制转换_牛客题霸_牛客网 (nowcoder.com)](https://www.nowcoder.com/practice/0337e32b1e5543a19fa380e36d9343d7?tpId=60&tqId=29473&tPage=1&ru=/kaoyan/retest/1001&qru=/ta/tsing-kaoyan/question-ranking) **描述** 将一个长度最多为30位数字的十进制非负整数转换为二进制数输出。 **输入描述**: 多组数据,每行为一个长度不超过30位的十进制非负整数。 (注意是10进制数字的个数可能有30个,而非30bits的整数) **输出描述**: 每行输出对应的二进制数。 思路:由于长度太长,肯定得用字符串来处理;模2余数好算,看最后一位即可;**难点**在于除以2后的结果 单独写了个chu2函数,从第一位开始遍历算起,如果除以2有余数,则下一位除以2前+10。**特殊情况**是第一位为1(1除外)的情况,需要最后去除第一位 ~~~c++ #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)](https://www.nowcoder.com/practice/fd972d5d5cf04dd4bb4e5f027d4fc11e?tpId=60&tqId=29498&tPage=2&ru=/kaoyan/retest/1001&qru=/ta/tsing-kaoyan/question-ranking) **描述** 对于一个十进制数A,将A转换为二进制数,然后按位逆序排列,再转换为十进制数B,我们称B为A的二进制逆序数。 例如对于十进制数173,它的二进制形式为10101101,逆序排列得到10110101,其十进制数为181,181即为173的二进制逆序数。 **输入描述**: 一个1000位(即10^999)以内的十进制数。 **输出描述**: 输入的十进制数的二进制逆序数。 ~~~ 输入: 173 输出: 181 ~~~ 思路:这题很难,对于十进制转二进制就上一题的方法,但是二进制也要转换为十进制,需要再写个字符串乘法和加法函数 直接照抄书上的了,模板题【做的脑CPU都快烧了】 ~~~c++ #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 [进制转换2_牛客题霸_牛客网 (nowcoder.com)](https://www.nowcoder.com/practice/ae4b3c4a968745618d65b866002bbd32?tpId=60&tqId=31034&tPage=2&ru=/kaoyan/retest/1001&qru=/ta/tsing-kaoyan/question-ranking) **描述** 将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 ~~~c++ #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; } ~~~ ## 八进制 [八进制_牛客题霸_牛客网 (nowcoder.com)](https://www.nowcoder.com/practice/eda051c1effc4dffa630bc8507f0c5f7?tpId=69&tqId=29677&tPage=2&ru=/kaoyan/retest/11002&qru=/ta/hust-kaoyan/question-ranking) 输入一个整数转为八进制,做了前面的题目此题1min就敲完了 ~~~c++ #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)](https://www.nowcoder.com/practice/9255c05d45b8406c9b588d7c57aa920b?tpId=63&tqId=29582&tPage=1&ru=/kaoyan/retest/9001&qru=/ta/zju-kaoyan/question-ranking) **描述** 输入两个**不超过整型定义**的非负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进制后再相加,此时就好做多了;我是使用的字符串形式来计算,看起来有些麻烦;此种方法的好处是**不用再去写转换字符串进制转换了(除法)** ~~~c++ #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(参考其他人题解),使得长度相等,这样不用重复两遍代码了 ~~~c++ #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(感觉有些作弊?) ~~~c++ #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; } ~~~ ## 进制转换 [进制转换_牛客题霸_牛客网 (nowcoder.com)](https://www.nowcoder.com/practice/deb19498bc644f53a6a99905ef5ee01d?tpId=61&tqId=29552&tPage=3&ru=%2Fkaoyan%2Fretest%2F1002&qru=%2Fta%2Fpku-kaoyan%2Fquestion-ranking) **描述** 写出一个程序,接受一个十六进制的数值字符串,输出该数值的十进制字符串(注意可能存在的一个测试用例里的多组数据)。 **输入描述**: 输入一个十六进制的数值字符串。 **输出描述**: 输出该数值的十进制字符串。 ### 法1:16进制字符转10进制 ~~~c++ #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的写法: ~~~c #include <stdio.h> int main() { int num = 0; while (~scanf ("%x", &num)) printf("%d\n", num); return 0; } ~~~ C++的写法: ~~~c++ #include <iostream> using namespace std; int main() { int num = 0; while (cin >> hex >> num) cout << dec << num << endl; return 0; } ~~~ ## 数制转换 [数制转换_牛客题霸_牛客网 (nowcoder.com)](https://www.nowcoder.com/practice/8ef02ef8571b417d8c311a87861f7a03?tpId=61&tqId=29528&tPage=2&ru=%2Fkaoyan%2Fretest%2F1002&qru=%2Fta%2Fpku-kaoyan%2Fquestion-ranking) **描述** 求任意两个不同进制非负整数的转换(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)。 思路: 1. 首先a进制字符串转为十进制数字(a2dec),其中设置flag位为0,直到遇到开头不为'0'的字符,即去除开头的0 2. 十进制数字转为b进制字符串(dec2b),需要注意dec % b如果大于等于10,要改为字母,因此做一个条件判断 3. 如果b=10,第一步完可以直接输出,不用再去dec2b里转为字符串了 ~~~c++ #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; } } ~~~ 最后修改:2023 年 06 月 10 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,请随意赞赏