Вы можете использовать MatchEvaluator. Идея состоит в том, что вы соответствуете или полному элементу одного из типов в вашем списке, или целевой строке. Если вы соответствуете целому элементу, вы просто подключаете его обратно - вам все равно, содержит ли он целевую строку. В противном случае вы вставляете текст замены.
public string GetReplacement(Match m) {
return m.Groups[1].Success ? m.Groups[1].Value : "YYY";
}
Regex r = new Regex( @"(?is)(<([abi]\b)[^<>]*>.*?</\2>)|XXX" );
string newString = r.Replace(oldString,
new MatchEvaluator(GetReplacement));
Но имейте в виду, что существует много обстоятельств, когда этот код не работает, даже в допустимом (X) HTML. Например, элемент может быть вложен в другой элемент того же вида, например:
<i>blah <i>blah</i> XXX</i>
Или начальный или конечный тег внутри комментария может сбить вас с толку:
<b>blah <!-- </b> --> XXX</b>
Вы можете справиться со многими потенциальными проблемами, усложнив регулярное выражение и код MatchEvaluator, но в конечном итоге вам придется либо принять несколько недостатков, либо переключиться на выделенный анализатор HTML, как рекомендованный Нолдорин.