华为OD机考题目《敏感字加密》100分题目
牛客上看到有个同学发了这么一道题,题目是 敏感字加密。
保险起见,我把他帖子截图进来了,感谢 @唐浮 提供的素材。处于个人兴趣,我简单做了一下。
首先谈一下我对这道题的理解: 这道题就是给你一个字符串,下划线分割,让你拆开,然后将第 k 个 改为 ****** 然后再拼回去。 特殊地,它会有一些 value中包含下划线,但是会被 双引号包围起来,就这一点难度。
如果没有这个特殊的点,那就更简单了。
我的思路是,写一个split函数(c++没有现成的split函数,需要自己写一个),把题目要求的分隔方式实现出来,分割到一个vector里去,然后,如果个数大于等于k,则将第k个改为 ******,如果个数不足k个,则输出ERROR 。
题目中没有说有多组用例,所以不需要用 while(cin>>xxxx)。
//我的主要代码片段如下 int main() { int k; string str; cin >> k >> str; vector<string> res; //split是自定义函数,实现了根据下划线分割字符串,特殊地,如果下划线被双引号包围,则不认为它是一个分隔符。 split(str, res, '_'); //如果分割出来的命令字个数不足k个,则无法将第K个加密,所以要输出ERROR if (k >= res.size()) { cout << "ERROR" << endl; return 0; } //将第k个加密 res[k] = "******"; do_cout(res);//按顺序并拼接下划线打印 return 0; }
其中,split函数我是这样实现的:
//split实现将str按照ch分隔,按顺序写入 rVector中。特殊的,如果ch被双引号包围,则不认为它是分隔符 void split(const string& str, vector<string>& rVector, char ch) { std::string::size_type startPos = 0; std::string::size_type endPos = str.find(ch, startPos); while (endPos != std::string::npos) { rVector.push_back(str.substr(startPos, endPos - startPos)); startPos = endPos + 1; if (str[startPos] == '\"') { endPos = str.find('\"', startPos+1); endPos++;//题目规定必然能找到下一个双引号,所以无需做npos判断。 } else { endPos = str.find(ch, startPos); } } rVector.push_back(str.substr(startPos)); }
最后,我的打印函数是这样写的,这个就没啥技术含量了,就是注意最后不要多一个下划线。
void do_cout(vector<string>& res) { //to do for (int i = 0; i < res.size(); i++) { cout << res[i]; if (i != res.size() - 1) { cout << "_"; } } cout << endl; }
需要注意的是,被调用的函数,要写在 main函数上方(因为都在cpp文件中),所以整体下来是这样的(代码已忽略 包含的头文件):
void split(const string& str, vector<string>& rVector, char ch) { std::string::size_type startPos = 0; std::string::size_type endPos = str.find(ch, startPos); while (endPos != std::string::npos) { rVector.push_back(str.substr(startPos, endPos - startPos)); startPos = endPos + 1; if (str[startPos] == '\"') { endPos = str.find('\"', startPos+1); endPos++;//题目规定必然能找到下一个双引号,所以无需做npos判断。 } else { endPos = str.find(ch, startPos); } } rVector.push_back(str.substr(startPos)); } void do_cout(vector<string>& res) { //to do for (int i = 0; i < res.size(); i++) { cout << res[i]; if (i != res.size() - 1) { cout << "_"; } } cout << endl; } int main() { int k; string str; cin >> k >> str; vector<string> res; //split是自定义函数,实现了根据下划线分割字符串,特殊地,如果下划线被双引号包围,则不做分割。 split(str, res, '_'); if (k >= res.size()) { cout << "ERROR" << endl; return 0; } res[k] = "******"; do_cout(res);//按顺序并拼接下划线打印 return 0; }
如果题目中说明“注意:题目包含多组用例”
则 整体的main函数要改为这样():
int main() { int k; string str; while (cin >> k >> str)//将此处改为 while,VS2017在本地退不出,但实际牛客网的用例可以过并可以退出。 { vector<string> res; split(str, res, '_'); if (k >= res.size()) { cout << "ERROR" << endl; continue;//这里不再是return 0,而是 continue,表示这个循环到此结束,继续下一个循环。 } res[k] = "******"; do_cout(res); } return 0; }
如果题目中,没有说明 命令字里包含 下划线,则 只需要修改split函数即可:
void split(const std::string& str, char ch, std::vector<std::string>& rVector) { std::string::size_type startPos = 0; std::string::size_type endPos = std::string::npos; while ((endPos = str.find(ch, startPos)) != std::string::npos) { rVector.push_back(str.substr(startPos, endPos - startPos)); startPos = endPos + 1; } rVector.push_back(str.substr(startPos)); }
上述代码并未经过实际验证是否可以拿满分,如果有瑕疵,可以稍作调试和修改。
本人已验证 当输入字符串为 a_"1_2"_3_""_5 时, k = 0 1 2 3 4 5 的 五个场景,结果如下:
有兴趣的同学可以用其他语言试一下。
楼主提供的三道题中,另外两道题,等有空了我再试试。
如果大家有更好的写法,欢迎留言讨论。
网友提供的一份java代码,PS:个人认为这个代码毫无可读性,所有方法全部平铺到一个main函数里了,有很大的优化空间。
import java.util.*; public class Main{ public static void main(String[] args){ Scanner scanner=new Scanner(System.in); int k=Integer.parseInt(scanner.nextLine()); String s= scanner.nextLine(); //用下面的for循环将s拆分开,建议把这一段分装一个函数出去。 ArrayList<String> coms=new ArrayList<>(); for(int i=0;i<s.length();){ //如果遇到双引号,则去找下一个双引号,两个双引号之间(含双引号)作为本次命令字 if(s.charAt(i)=='"'){ int j=i+1; for(;j<s.length();j++){ if(s.charAt(j)=='"'){ break; } } coms.add(s.substring(i,j+1)); i=j+1; continue; } //如果s的第i位不是下划线,也不是双引号,则找下一个下划线,并把第i位到下一个下划线之间位置一个命令字。 if(s.charAt(i)!='_'&&s.charAt(i)!='"'){ int j=i+1; for(;j<s.length();j++){ if(s.charAt(j)=='_'){ break; } } coms.add(s.substring(i,j)); i=j; continue; } //如果是下划线,则跳过 if(s.charAt(i)=='_'){ i++; continue; } } //如果超限,则打印ERROR String out=""; if(k>=coms.size()){ System.out.println("ERROR"); return; } //用最笨的方式将第k个命令字改为****** for(int i=0;i<coms.size()-1;i++){ if(i!=k){ out=out+coms.get(i)+"_"; }else { out=out+"******"+"_"; } } //用最笨的方法拼接打印元素out if(k==coms.size()-1){ out=out+"******"; }else { out=out+coms.get(coms.size()-1); } System.out.println(out); return; } }