これをインプレースで効率的に行うことができます。適切な間隔でバッファ内の文字を上書きする必要があり、論理的にバッファをsetLength
. かなり複雑になりますが、インプレースでO(N)
.
delete
/を使用する代わりに上書きしたい理由insert
はO(N^2)
、物事を不必要に移動する必要があるためです。
このアウトオブプレースを実行するのは非常に簡単O(N)
ですが、2 次バッファーが必要になるため、必要なスペースが 2 倍になります。
コンセプトの証明
簡単な概念実証を次に示します。とremoveIntervals
を取ります。それぞれが値のペア(半分の範囲、排他的な上限) であると想定されます。線形時間とインプレースでは、これらの間隔は単純な によってから削除されます。これは、間隔がソートされていて重複していない場合に機能し、左から右に処理されます。StringBuffer
int[][] intervals
int[]
{ start, end }
StringBuffer
overwrite
次にバッファは で短縮されsetLength
、削除された文字数だけ切り取られます。
static void overwrite(StringBuffer sb, int dst, int srcFrom, int srcTo) {
for (int i = srcFrom; i < srcTo; i++) {
sb.setCharAt(dst++, sb.charAt(i));
}
}
static int safeGet(int[][] arr, int index, int defaultValue) {
return (index < arr.length) ? arr[index][0] : defaultValue;
}
static void removeIntervals(StringBuffer sb, int[][] intervals) {
int dst = safeGet(intervals, 0, 0);
int removed = 0;
for (int i = 0; i < intervals.length; i++) {
int start = intervals[i][0];
int end = intervals[i][1];
int nextStart = safeGet(intervals, i+1, sb.length());
overwrite(sb, dst, end, nextStart);
removed += end - start;
dst += nextStart - end;
}
sb.setLength(sb.length() - removed);
}
次に、次のようにテストできます。
String text = "01234567890123456789";
int[][][] tests = {
{ { 0, 5, },
}, // simple test, removing prefix
{ { 1, 2, }, { 3, 4, }, { 5, 6, }
}, // multiple infix removals
{ { 3, 7, }, { 18, 20, },
}, // suffix removal
{
}, // no-op
{ { 0, 20 },
}, // remove whole thing
{ { 7, 10 }, { 10, 13 }, {15, 15 },
}, // adjacent intervals, empty intervals
};
for (int[][] test : tests) {
StringBuffer sb = new StringBuffer(text);
System.out.printf("> '%s'%n", sb);
System.out.printf("- %s%n", java.util.Arrays.deepToString(test));
removeIntervals(sb, test);
System.out.printf("= '%s'%n%n", sb);
}
これは出力します ( ideone.com で見られるように):
> '01234567890123456789'
- [[0, 5]]
= '567890123456789'
> '01234567890123456789'
- [[1, 2], [3, 4], [5, 6]]
= '02467890123456789'
> '01234567890123456789'
- [[3, 7], [18, 20]]
= '01278901234567'
> '01234567890123456789'
- []
= '01234567890123456789'
> '01234567890123456789'
- [[0, 20]]
= ''
> '01234567890123456789'
- [[7, 10], [10, 13], [15, 15]]
= '01234563456789'
間隔を取得する
この特定のケースでは、インターバルを ( を使用して) 予備パスで構築するか、絶対に必要な場合はindexOf
プロセス全体を 1 つのパスで実行できます。ポイントは、これは間違いなく線形時間でインプレースで実行できることです (絶対に必要な場合は、シングルパスで実行できます)。
場違いな解決策
これは、セカンダリ バッファーと正規表現を使用する場違いです。そのシンプルさから検討のために提供されます。さらなる最適化が必要であることが証明されていない限り (証拠のプロファイリング結果の後)、これで十分です。
String text =
"DEL: line1\n" +
"KEP: line2\r\n" +
"DEL: line3\n" +
"KEP: line4\r" +
"DEL: line5\r" +
"DEL: line6\r" +
"KEP: line7\n" +
"DEL: line8";
StringBuffer sb = new StringBuffer(text);
Pattern delLine = Pattern.compile("(?m)^DEL:.*$");
String cleanedUp = delLine.matcher(sb).replaceAll("<deleted!>");
System.out.println(cleanedUp);
これは出力します ( ideone.com で見られるように):
<deleted!>
KEP: line2
<deleted!>
KEP: line4
<deleted!>
<deleted!>
KEP: line7
<deleted!>
参考文献