實現類 Emmet 的 HTML 縮寫展開。輸入縮寫字符串(含標籤名、id(#)、class(.)、子選擇器(>)、兄弟分組(())),輸出完整 HTML 標籤結構。約束:N ≤ 50,輸入長度 ≤ 120,輸出 ≤ 1000。
#id:id 屬性,每標籤最多一個。.class:class 屬性,可多個,按順序輸出。>:子層級嵌套。():分組,生成同級標籤。示例:div.aa#bb.cc.ee>p#g>span.d → 嵌套結構;div>(p#g1>span)(p#g2>span) → 同級 p 標籤。
定義遞歸函數 parse(l, r) 處理子串 [l, r):
#、.、()、>:# 後跟 id 名。. 後跟 class 名,累積。( 找到匹配 ),遞歸解析內容作為同級分組(一組可包含多個同級標籤)。> 後跟子標籤,遞歸解析並嵌套輸出。<tag id="..." class="...">(id 優先於 class)。</tag>。string s;
bool isAlnum(char c) { return isalnum(c); }
void parse(int l, int r) {
string tag, id, cls;
int p = l;
// 1. 讀標籤名
while (p < r && isAlnum(s[p])) ++p;
tag = s.substr(l, p - l);
// 2. 解析屬性與結構
while (p < r) {
if (s[p] == '#') { // id
int st = ++p;
while (p < r && isAlnum(s[p])) ++p;
id = s.substr(st, p - st);
} else if (s[p] == '.') { // class
int st = ++p;
while (p < r && isAlnum(s[p])) ++p;
cls += (cls.empty() ? "" : " ") + s.substr(st, p - st);
} else if (s[p] == '(') { // 分組
int st = ++p, cnt = 1;
while (p < r) {
if (s[p] == ')') --cnt;
else if (s[p] == '(') ++cnt;
if (cnt == 0) break;
++p;
}
parse(st, p); // 遞歸處理分組內容(同級)
++p; // 跳過 ')'
} else if (s[p] == '>') { // 子標籤
++p;
// 輸出當前標籤開始
cout << "<" << tag;
if (!id.empty()) cout << " id=\"" << id << "\"";
if (!cls.empty()) cout << " class=\"" << cls << "\"";
cout << ">";
parse(p, r); // 遞歸處理子內容
cout << "" << tag << ">";
return; // > 後必為最後部分
} else break;
}
// 無 > 時輸出自閉或空標籤
cout << "<" << tag;
if (!id.empty()) cout << " id=\"" << id << "\"";
if (!cls.empty()) cout << " class=\"" << cls << "\"";
cout << ">" << tag << ">";
}
int main() {
int T; cin >> T; string dummy; getline(cin, dummy);
while (T--) {
getline(cin, s);
parse(0, s.size());
cout << endl;
}
}
() 內可包含多個同級標籤(由 > 分隔),遞歸處理時順序輸出。> 後必為最後部分,直接遞歸處理剩餘字符串。