#P3257. 文本统计分析(200分)
-
1000ms
Tried: 126
Accepted: 11
Difficulty: 7
所属公司 :
华为od
文本统计分析(200分)
题面描述:
题目要求统计一个文本文件中符合特定规则的文本数量。文本以分号(;)分隔,最后一条可以没有分号,但不能包含空文本。文本可以跨行,支持字符串(单双引号)和转义字符,也允许在字符串外部添加注释(以--开头)。输入为文本文件,输出为包含的文本数量。例如,给定一段包含SQL命令的文本,程序需计算有效命令的数量并输出。
解题思路详解
-
转义字符处理:在处理字符串时,首先需要去掉转义的双引号和单引号。这样可以确保在后续的成对引号判断中不受到干扰。
-
去除成对引号内容:通过去除成对的双引号和单引号及其内部内容,可以简化后续对注释和分号的处理。这一步骤确保了注释和文本之间不会互相影响。
-
注释处理:去除以
--开头的注释内容是重要的一步。注释可以出现在任何位置,确保其被正确去除后,后续的分号计数不会被误判。 -
处理空文本:在行处理中,要去掉空白字符。通过这一处理,可以避免出现只有分号而没有实际文本的情况。
-
处理连续分号:在文本中,连续的分号需被替换为一个分号,以便于正确计数文本数量。例如,对于
abc;;def,应被视为两个文本。 -
去除开头分号:行中若以分号开头,这些分号不应计入有效文本数量。例如,
;abc需处理为abc。 -
统计文本数量:在经过上述步骤后,剩余的分号即代表文本数量。程序将基于这些分号统计有效的文本数。
代码说明
你的代码设计得当,逐个字符扫描的方式非常有效,能确保每个字符的状态都被正确追踪。以下是对代码中各部分的具体分析:
-
状态标志:使用
sq_flag和dq_flag变量来记录当前是否处于单引号或双引号内,确保对文本和注释的分离处理。 -
注释检测:通过检测
'-'字符后跟随的'-',判断是否为注释开头,从而可以直接跳过后续的内容。 -
文本计数逻辑:当检测到分号时,只有在前面存在非空内容的情况下,才增加文本数量。这种设计避免了将单独的分号或空文本计入有效文本数量。
-
最后一个文本处理:在遍历结束后,如果最后存在有效文本(即有非空内容),则要确保该文本被计入数量。
cpp
#include <bits/stdc++.h>
using namespace std;
int solution(string &s) {
// 记录题解,即文件中包含的文本数量
int ans = 0;
// 当前文本的"非空"内容长度
int content_length = 0;
// 单引号开闭状态,当前解法默认单引号必然成对出现
bool sq_flag = false;
// 双引号开闭状态,当前解法默认双引号必然成对出现
bool dq_flag = false;
// 定义一个指针 i 用于扫描文件内容
int i = 0;
while (i < s.length()) {
// 当前正在被扫描的字符
char c = s[i];
// 用 if-else 替代 switch
if (c == '\\') { // 如果扫描到转义\符号
i++;
} else if (c == '\"') { // 如果扫描到双引号
if (!sq_flag) { // 如果该双引号不在成对单引号内部
dq_flag = !dq_flag; // 则双引号开闭状态取反
}
} else if (c == '\'') { // 如果扫描到单引号
if (!dq_flag) { // 如果该单引号不在成对双引号内部
sq_flag = !sq_flag; // 则单引号开闭状态取反
}
} else if (c == '-') { // 如果扫描到 '-'
if (!sq_flag && !dq_flag) { // 如果该 '-' 处于成对双向号,或成对单引号内部,则不是注释符号
if (++i < s.length() && s[i] == '-') { // 如果该 '-' 后面紧跟着的字符还是 '-',则确定当前遇到注释符号
while (++i < s.length() && s[i] != '\n') {} // 注释符号后面的内容(直到行末)都不算文本,即不会增加文本数量
}
}
} else if (c == ';') { // 如果扫描到 ';'
if (!sq_flag && !dq_flag && content_length > 0) { // 该分号不处于成对单引号或成对双引号内部,且分号前存在非空内容
ans++; // 则文本数量++
content_length = 0; // 继续统计下一个文本
}
} else { // 如果扫描到其他字符
if (!isspace(c)) { // 该字符非空白字符
content_length++; // 则非空文本内容长度++
}
}
// 继续扫描下一个字符
i++;
}
// 题目说最后一个文本可以没有分号,即最后的文本只有存在非空内容,即为一个文本
if (content_length > 0) {
ans++;
}
return ans;
}
int main() {
// 将文件内容压缩为一行,换行使用换行符\n代替
string lines;
while (!cin.eof()) {
string line;
getline(cin, line);
lines += line + '\n';
}
cout << solution(lines) << endl;
return 0;
}
python
def solution(s):
# 记录文本数量
ans = 0
# 当前文本的非空内容长度
content_length = 0
# 单引号和双引号的开闭状态
sq_flag = False # 单引号状态
dq_flag = False # 双引号状态
i = 0
while i < len(s):
c = s[i]
if c == '\\': # 处理转义符
i += 1 # 跳过下一个字符
elif c == '\"': # 双引号处理
if not sq_flag: # 如果不在单引号内部
dq_flag = not dq_flag # 切换双引号状态
elif c == '\'': # 单引号处理
if not dq_flag: # 如果不在双引号内部
sq_flag = not sq_flag # 切换单引号状态
elif c == '-': # 处理注释
if not sq_flag and not dq_flag: # 如果不在任何引号内部
if i + 1 < len(s) and s[i + 1] == '-': # 检查下一个字符
while i < len(s) and s[i] != '\n': # 跳过注释内容
i += 1
elif c == ';': # 分号处理
if not sq_flag and not dq_flag and content_length > 0: # 检查内容长度
ans += 1 # 增加文本数量
content_length = 0 # 重置内容长度
else: # 其他字符处理
if not c.isspace(): # 处理非空白字符
content_length += 1 # 增加内容长度
# 继续扫描下一个字符
i += 1
# 检查最后一个文本是否存在
if content_length > 0:
ans += 1
return ans
def main():
# 将文件内容压缩为一行
lines = []
try:
while True:
line = input() # 读取输入
lines.append(line)
except EOFError:
pass # 处理输入结束
# 将所有行连接为一个字符串
full_text = '\n'.join(lines)
# 计算文本数量并输出
print(solution(full_text))
if __name__ == "__main__":
main()
java
import java.util.Scanner;
public class Main {
public static int solution(String s) {
// 记录文本数量
int ans = 0;
// 当前文本的非空内容长度
int contentLength = 0;
// 单引号和双引号的开闭状态
boolean sqFlag = false; // 单引号状态
boolean dqFlag = false; // 双引号状态
int i = 0;
while (i < s.length()) {
char c = s.charAt(i);
if (c == '\\') { // 处理转义符
i++;
} else if (c == '\"') { // 双引号处理
if (!sqFlag) { // 如果不在单引号内部
dqFlag = !dqFlag; // 切换双引号状态
}
} else if (c == '\'') { // 单引号处理
if (!dqFlag) { // 如果不在双引号内部
sqFlag = !sqFlag; // 切换单引号状态
}
} else if (c == '-') { // 处理注释
if (!sqFlag && !dqFlag) { // 如果不在任何引号内部
if (i + 1 < s.length() && s.charAt(i + 1) == '-') { // 检查下一个字符
while (i < s.length() && s.charAt(i) != '\n') { // 跳过注释内容
i++;
}
}
}
} else if (c == ';') { // 分号处理
if (!sqFlag && !dqFlag && contentLength > 0) { // 检查内容长度
ans++; // 增加文本数量
contentLength = 0; // 重置内容长度
}
} else { // 其他字符处理
if (!Character.isWhitespace(c)) { // 处理非空白字符
contentLength++; // 增加内容长度
}
}
// 继续扫描下一个字符
i++;
}
// 检查最后一个文本是否存在
if (contentLength > 0) {
ans++;
}
return ans;
}
public static void main(String[] args) {
// 使用 Scanner 读取输入
StringBuilder lines = new StringBuilder();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
lines.append(line).append('\n');
}
scanner.close();
// 计算文本数量并输出
System.out.println(solution(lines.toString()));
}
}
题目内容
有一个文件,包含以一定规则写作的文本,请统计文件中包含的文本数量。
规则如下:
-
文本以 ";" 分隔,最后一条可以没有 ";" ,但空文本不能算语句,比如
COMMAND A; ;
只能算一条语句。 注意,无字符/空白字符/制表符都算作"空"文本;
-
文本可以跨行,比如下面,是一条文本,而不是三条
COMMAND A
AND
COMMAND B;
-
文本支持字符串,字符串为成对的单引号(')或者成对的双引号("),字符串可能出现用转义字符()处理的单双引号("your input is"")和转义字符本身,比如
COMMAND A "Say "hello"";
-
支持注释,可以出现在字符串之外的任意位置注释以”--“开头,到换行结束,比如
COMMAND A; -- this is comment
COMMAND -- comment
A AND COMMAND B;
注意字符串内的”--“,不是注释。
输入描述
文本文件
输出描述
包含的文本数量
样例1
输入
COMMAND TABLE IF EXISTS "UNITED STATE";
COMMAND A GREAT (
ID ADSAB,
download\_length INTE-GER, -- test
file\_name TEXT,
guid TEXT,
mime\_type TEXT,
notifica-tionid INTEGER,
original\_file\_name TEXT,
pause\_reason\_type INTEGER,
resumable\_flag INTEGER,
start\_time INTEGER,
state INTEGER,
folder TEXT,
path TEXT,
total\_length INTE-GER,
url TEXT
);
输出
2