CodeForces Problem - Segments Removal
题目类型:链表、Set
题意
给定一个整数序列,每次选择元素相同的最长的连续的子串然后删除,删除后左右两边的部分会拼接起来,如果有多个这样的子串则选择最左边的。求按照如上规则多少次操作后源串会变为空串。
分析
首先将连续的子串处理为 num 和 cnt 表示,然后用静态链表存储,并用 TreeSet 保存,使得 set 最后一个元素为长度最长且是最左边得子串。然后模拟执行上面得操作,每次删除一个子串后将其链表中左右两边得节点连起来,并判断是否相同,如果相同则进行合并,并更新 set 中得值。
代码
static int[] r;
static int[] l;
static int[] cnt;
static int[] num;
static boolean[] vis;
static int idx;
static TreeSet<Node> q;
public static void solve() throws IOException {
int n = nextInt();
init(n);
int[] a = new int[n];
for (int i = 0; i < n; i++) a[i] = nextInt();
q = new TreeSet<>();
for (int i = 0; i < n; i++) {
int j = i;
while (j + 1 < n && a[i] == a[j + 1]) j++;
q.add(new Node(j - i + 1, idx));
add(a[i], j - i + 1);
i = j;
}
int ans = 0;
while (!q.isEmpty()) {
Node e = q.last();
q.remove(e);
if (vis[e.idx] || e.cnt == 0) continue;
ans++;
remove(e.idx);
}
pw.println(ans);
}
public static void remove(int k) {
vis[k] = true;
r[l[k]] = r[k];
l[r[k]] = l[k];
k = l[k];
if (num[k] == num[r[k]]) {
q.remove(new Node(cnt[k], k));
vis[r[k]] = true;
cnt[k] += cnt[r[k]];
r[k] = r[r[k]];
l[r[k]] = k;
q.add(new Node(cnt[k], k));
}
}
public static void add(int x, int c) {
num[idx] = x;
cnt[idx] = c;
r[idx] = idx + 1;
l[idx] = idx - 1;
idx++;
}
public static void init(int n) {
int N = 200010;
vis = new boolean[N];
r = new int[N];
l = new int[N];
cnt = new int[N];
num = new int[N];
idx = 2;
}
/*****************************************************************************************/
static class Node implements Comparable<Node>{
int cnt;
int idx;
public Node(int cnt, int idx) {
this.cnt = cnt;
this.idx = idx;
}
@Override
public int compareTo(Main.Node o) {
if (this.cnt != o.cnt) return this.cnt - o.cnt;
else return o.idx - this.idx;
}
}