实不相瞒,这道题卡了我好几天。不过让我没有想通的并不是算法的实现,而是正则表达式。
我的主要思路是:首先,从模板中提取可能出现的变量。然后,再根据后续数据为每个变量进行赋值(或者说建立映射)。最后遍历模板进行替换。注意:由于题目明确要求不会出现递归生成的情况,因此不可以反复对同一个模板字符串进行正则表达式的替换操作。不信的话可以用以下测试用例试试。
11 2
<!DOCTYPE html>
<html>
<head>
<title>User {{ name }}</title>
</head>
<body>
<h1>{{ name }}</h1>
<p>Email: <a href="mailto:{{ email }}">{{ email }}</a></p>
<p>Address: {{ address }}</p>
</body>
</html>
name "David Beckham {{ email }}"
email "david@beckham.com"
令我纠结的地方在于对于变量名的正则表达式的提取。我本来是使用“[_a-zA-Z][_0-9a-zA-Z]*”来提取变量名的,可是这样只能得40分。后来我又尝试了其他方式,终于在最后发现使用“[_a-zA-Z]?[_0-9a-zA-Z]+”就没有任何问题了。不过,后来我发现由于题目提示每一个变量名必然是合法的,因此可以不必在正则表达式上检验变量名的合法性。甚至使用“[^\\}]”和“[_0-9a-zA-Z]+”来提取变量名都没有问题。不过我个人还是倾向于严谨一些的方式,毕竟这样也有助于学习正则表达式。
代码部分参考于日沉云起:CCF认证201509-3模板生成系统
具体代码如下:
#include <iostream>
#include <regex>
#include <vector>
#include <string>
#include <map>
using namespace std;
int main()
{
//FILE *stream;
//freopen_s(&stream, "data.txt", "r", stdin);
int m, n;
cin >> m >> n;
getchar();
vector<string> modle; //存放模板
map<string, string> vars; //存放变量名及其值
for(int i = 0; i < m; i++)
{
string s;
getline(cin, s);
modle.push_back(s);
smatch result;
if(regex_search(s, result, regex("\\{\\{ ([_a-zA-Z]?[_0-9a-zA-Z]+) \\}\\}")))
{
for(unsigned i = 1; i < result.size(); i++)
{
vars[(string)result[i]] = ""; //初始化变量对应的值为空
}
}
}
for(int i = 0; i < n; i++)
{
string var, value;
cin >> var;
getchar();
getline(cin, value);
value.erase(value.size() - 1, 1); //去除右端双引号
value.erase(0, 1); //去除左端双引号
vars[var] = value; //对变量进行赋值
}
smatch result;
for(string &i : modle)
{
auto j = i.cbegin(); //传入regex_search的迭代器需要是const_iterator类型
while(regex_search(j, i.cend(), result, regex("\\{\\{ ([_a-zA-Z]?[_0-9a-zA-Z]+) \\}\\}")))
{
//输出从开始搜索的位置到匹配成功的子字符串的起始位置的所有字符
for(; j != result[0].first; j++)
cout<<*j;
auto k = vars.find(result[1]); //查看vars中是否有相应的变量名
if(k != vars.end()) cout << k->second;
j = result[0].second; //将下一个开始搜索位置更新为当前匹配成功的子字符串的末尾位置
}
for(; j != i.cend(); j++) //输出剩余字符
cout << *j;
cout << endl;
}
//fclose(stream);
return 0;
}