```java
import java.util.*;
public class ParseHtml {
/*
*
input aa<a>bbb<b>ccc</b>gg</a>zzz
output :
1. aa
2. a: bbb
3. a+b: ccc
4. a: gg
5. zzz
* */
public static void main(String[] args){
ParseHtml parseHtml = new ParseHtml();
String input = "aa<a>bbb<b>ccc</b>gg</a>zzz";
try {
List<HtmlText> list = parseHtml.parseSeq(input);
for (int i = 0; i < list.size(); i++) {
HtmlText text = list.get(i);
System.out.println(text.content + " " + text.tags);
}
} catch (Throwable t) {
System.out.println(t);
}
}
public List<HtmlText> parseSeq(String input) throws Exception {
StringBuilder value = new StringBuilder();
ArrayList<String> tags = new ArrayList<>();
ArrayList<HtmlText> ans = new ArrayList<>();
Stack<String> sk = new Stack<>();
int idx = 0, N = input.length();
while (idx < N) {
char cur = input.charAt(idx);
if (cur == '<') {
if (value.length() > 0) {
ans.add(new HtmlText(value.toString(), new HashSet<String>(tags)));
}
value = new StringBuilder();
Pair<String, Integer> pair = getTag(input, idx);
if (isOppositeTag(pair.a)) {
if (sk.isEmpty()) throw new Exception("syntax error: only / tag " + pair.a);
String tag = sk.pop();
if (!isMatchOppositeTag(tag, pair.a)) {
throw new Exception("syntax error: " + pair.a + " not match any tag");
} else {
tags.remove(tags.size() - 1);
}
} else {
tags.add(pair.a);
sk.add(pair.a);
}
idx = pair.b + 1;
} else {
value.append(cur);
idx++;
}
}
if (!tags.isEmpty()) {
throw new Exception("syntax error: " + tags + " not match any tag");
}
if (value.length() > 0) {
ans.add(new HtmlText(value.toString(), new HashSet<>()));
}
return ans;
}
private boolean isOppositeTag(String a) {
return a.contains("/");
}
private boolean isMatchOppositeTag(String a, String b) {
int aIdx = 0, bIdx = 0;
while (aIdx < a.length() && bIdx < b.length()) {
char aChar = a.charAt(aIdx);
char bChar = b.charAt(bIdx);
if (aChar == '/') {
aIdx++;
} else if (bChar == '/') {
bIdx++;
} else if (aChar != bChar) {
return false;
} else {
aIdx++;
bIdx++;
}
}
return aIdx == a.length() && bIdx == b.length();
}
private Pair<String, Integer> getTag(String input, Integer curIdx) throws Exception {
if (input.charAt(curIdx) != '<') throw new Exception("tag doesn't start from <");
curIdx++;
String ans = "";
while (input.charAt(curIdx) != '>') {
ans += input.charAt(curIdx++);
}
return new Pair(ans, curIdx);
}
}
class Pair<A, B> {
A a;
B b;
Pair(A a, B b) {
this.a = a;
this.b = b;
}
}
class HtmlText {
String content;
Set<String> tags;
HtmlText(String a, Set<String> b) {
this.content = a;
this.tags = b;
}
}
```
output
```
aa []
bbb [a]
ccc [a, b]
gg [a]
zzz []
```
```java=
public static void main(String[] args) {
TestQ tt = new TestQ();
tt.test2(5, 2, 4);
System.out.println("======");
tt.test2(5, 2, 3);
}
public void test2(int N, int X, int Y) {
Graph graph = new Graph();
for (int i = 1; i <= N; i++) {
Node node = new Node(i);
graph.map.put(i, node);
}
int tmp = Y, tmp1 = X;
Y = Math.max(tmp, tmp1);
X = Math.min(tmp, tmp1);
Node node1 = graph.map.get(X);
Node node2 = graph.map.get(Y);
Edge edge;
edge = new Edge(node1, node2, 1);
node1.edges.add(edge);
graph.edges.add(edge);
for (int k : graph.map.keySet()) {
Node node = graph.map.get(k);
Node nextNode = graph.map.get(node.value + 1);
if (nextNode == null) continue;
if (nextNode.value == Y && node.value == X) continue;
Edge ee = new Edge(node, nextNode, 1);
node.edges.add(ee);
graph.edges.add(ee);
}
int ans = 0;
for (int i = 0; i < graph.edges.size(); i++) {
if (graph.edges.get(i).cost == 1) {
ans++;
}
}
System.out.println("1 : " + ans);
for (int dis = 2; dis < N; dis++) {
int tmpAns = 0;
for (Integer k : graph.map.keySet()) {
Node node = graph.map.get(k);
tmpAns += traverse(node, dis, X, Y);
}
System.out.println(dis + " : " + tmpAns);
}
}
public int traverse(Node curNode, int dis, int X, int Y) {
if (dis == 0) {
return 1;
}
if (curNode.value == X && curNode.value + dis >= Y) {
//fast
List<Edge> edges = curNode.edges;
Node nextNode = null;
for (int fi = 0; fi < edges.size(); fi++) {
if (nextNode == null) {
nextNode = edges.get(fi).e;
} else if (nextNode.value < edges.get(fi).e.value) {
nextNode = edges.get(fi).e;
}
}
if (nextNode == null) {
return 0;
}
return traverse(nextNode, dis - 1, X, Y);
} else {
// slow
List<Edge> edges = curNode.edges;
int tmp = 0;
for (int fi = 0; fi < edges.size(); fi++) {
Node nextNode = edges.get(fi).e;
tmp += traverse(nextNode, dis - 1, X, Y);
}
return tmp;
}
}
public class Edge {
Node s;
Node e;
int cost = 0;
Edge(Node s, Node e, int cost) {
this.s = s;
this.e = e;
this.cost = cost;
}
}
public class Node {
int value = -1;
List<Edge> edges = new ArrayList<>();
public Node(int value) {
this.value = value;
}
}
public class Graph {
Map<Integer, Node> map = new LinkedHashMap<>(); //1:node, 2: node
List<Edge> edges = new ArrayList<>();// 1-2, 2-3
}
```
output
```
1 : 5
2 : 4
3 : 1
4 : 0
======
1 : 4
2 : 3
3 : 2
4 : 1
```