I tested both locally and the method using lookbehind was about 25% slower.
, lookahead lookbehind, 10% :
s = Regex.Replace(s, @"(;|!|\?) (?=\.{3})", "$1");
, , . , , . , , , .
, "blah; ... foo ...; bar bar ? ..." 1000 , 100 .
0.944s No lookarounds Regex.Replace(s, @"(;|!|\?) \.{3}", "$1...")
1.027s Look ahead Regex.Replace(s, @"(;|!|\?) (?=\.{3})", "$1")
1.210s Look behind Regex.Replace(s, @"(?<=(;|!|\?)) \.{3}", "...")
1.124s Both Regex.Replace(s, @"(?<=(;|!|\?)) (?=\.{3})", "")