目录
板块零:输入输出流
情况一:读取字符串和读取行混用的时候
情况二:关于识别空行
第一题:hdoj2072
情况三:用char数组接受getline函数的输入流
情况四:关于汉字
补充练习:
第一题:单词反转(对行,pe地狱)
情况五:关于getchar()读取到文件尾
第一题:排序
板块一:字典树
注意:
操作1:建树
操作2:查找
阴间的输入和输出。
#include
using namespace std;
int main() {
string a, b;
cin >> a;
cin.get();//清除回车,否则下面的getline会因为cin残留的回车符直接结束,输出回车
getline(cin, b);
cout << a << '\n';
cout << b << '\n';
return 0;
}
cin会残留回车符,就像scanf一样。
#include
using namespace std;
int main() {
string a;
getline(cin, a);
if (a == "") {
cout << "YES\n";
} else {
cout << "NO\n";
}
return 0;
}
getline对空行畅行无阻,并不会停下来,不会异常返回。
专治阴间输入输出流练习题:
注意坑点可能有多个空格,主要这里是每行一组,以#结束。
#include
using namespace std;
int main() {
char ch;
set s;
string a;
int flag = 0;//如果前面有空格的话
while ((ch = getchar()) != '#') {//如果不是#
if (ch <= 'z' && ch >= 'a') {
if (flag) {//如果后面有字母,而且,前面是有空格
s.insert(a);
flag = 0;
a = "";
}
a += ch;
} else if (ch == ' ') {//如果最开始就是空格
if (a != "") {//而且,a不是空串,flag等于1
flag = 1;
}
} else if (ch == '\n') {//如果读到回车
if (a != "") {//而且还有一个串
s.insert(a);
a = "";
flag = 0;//注意置零以防对下一组数据产生影响
}
cout << s.size() << '\n';
s.clear();//清空
}
}
return 0;
}
getline()函数是只能接受string类型的。 不信可以试试:p
#include
#include
using namespace std;
int main() {
string a;
char b[1000];
getline(cin, a);
return 0;
}
汉字占两个char,而且,两个char变量的值均为负数。
汉字统计
#include
using namespace std;
const int MAX = 1e6 + 5;
char s[MAX];
int main() {
int t;
cin >> t;
cin.get();
while (t--) {
gets(s + 1);
long long ans = 0;
for (int i = 1; i < strlen(s + 1); i++) {
if (s[i] >= 0 && s[i] <= 127) continue;
ans++;
i++;
}
cout << ans << '\n';
}
return 0;
}
#include
using namespace std;
int main() {
int t;
cin >> t;
getchar();
while (t--) {
string s = "";
char ch;
while ((ch = getchar()) != '\n') {
if (ch == ' ') {
for (int i =s.size() - 1; i >= 0; i--) {
if (s[i] == ' ') continue;//小心空格
cout << s[i];
}
cout << ' ';
s = "";
}
s += ch;
}
for (int i = s.size() - 1; i >= 0; i--) {
if (s[i] == ' ') continue;//小心空格
cout << s[i];
}
cout << '\n';
}
return 0;
}
注意while ((ch = getchar()) != EOF) ch和getchar之间需要括号,否则优先级会出问题。
#include
using namespace std;
int main() {
// ios::sync_with_stdio(false);
// cin.tie(0);
priority_queue, greater > q;
char ch;
int flag = 0, sum = 0;
while ((ch = getchar()) != EOF) {
if (ch != '5' && ch != '\n') {
flag = 1;
}
if (flag && ch != '5' && ch != '\n') {
sum = 10 * sum + ch - '0';
}
if (flag && ch == '5') {
flag = 0;
q.push(sum);
sum = 0;
}
if (ch == '\n') {
if (flag) {
q.push(sum);
sum = 0;
flag = 0;
}
int c = q.top();
q.pop();
cout << c;
while (!q.empty()) {
c = q.top();
q.pop();
cout << ' ' << c;
}
cout << '\n';
}
}
return 0;
}
字典树是一种前缀树。有邻接矩阵版本,指针链表版本和结构体版本(大概)。
关于数组开多大的问题,注意,这种方法的空间消耗很大。最大的空间不是最长子串而是所有的字母数,即单词数 * 最长单词长度,这样才稳妥不会RE。
板子:
void insert(string s) {
int p = 0;
for (int i = 0; i < s.size(); i++) {
int x = s[i] - 'a';
if (trie[p][x] == 0) trie[p][x] = ++id;
p = trie[p][x];
}
cnt[p]++;
}
trie是一个二维数组,不是字符数组,有点类似邻接矩阵。其中一维是26个字母的标号,第二维是节点的编号用变量p表示,id是一个编号,用来表示单词的先后关系,用来说明此处是否有单词。cnt数组用来统计此处的单词数目。
这个id的使用是关键,当trie走到零的时候,说明这是一个之前没有走到过的一个新的节点,id++,输入第一个单词的时候,比如“abc”id是变化是1 2 3 然后我们再次输入 cba的话,id 变化是 4 5 6但是如果输入的是a b d的话,则是输入a的时候会索引到“ab”的b的p是2,然后到d的时候p到了3,此时是d然后出现了一个新的编号7
下面是p右边是x | 0(a) | 1(b) | 2(c) | 3(d) |
0 | id = 1 1 | 4 | ||
1(p = id) | 2 2 | |||
2 | 3 | 7 | ||
3 | ||||
4 | 5 | |||
5 | 6 | |||
6 | ||||
7 |
三种颜色表示了三次输入,填表的过程。id是独立且唯一的,我们给前缀不同的单词进行标号,每当出现一种新的前缀就给一个新的编号。这种写法感觉空间的浪费还是有点大的。
int find(string s) {
int p = 0;
for (int i = 0; i < s.size(); i++) {
int x = s[i] - 'a';
if (trie[p][x] == 0) return 0;
p = trie[p][x];
}
return cnt[p];
}
和建树基本相同,