Я поясню частину регулярних виразів поза тестом на первинність: наступний регулярний вираз, поданий а, String sякий складається з повторення String t, знахідки t.
System.out.println(
"MamamiaMamamiaMamamia".replaceAll("^(.*)\\1+$", "$1")
); // prints "Mamamia"
Як це працює в тому , що регулярні вирази захоплює (.*)в\1 , а потім бачить , якщо там \1+після нього. Використання ^та $гарантує, що відповідність має бути з усієї рядка.
Таким чином, ми певним чином даємо String s, що є "кратним" String t, і регулярний вираз знайде таке t(найдовше можливо, оскільки \1жадібний).
Після того, як ви зрозумієте, чому працює цей регулярний вираз, тоді (ігноруючи першу чергу в регексе OP), пояснюючи, як він використовується для тестування первинності, це просто.
- Щоб перевірити первинність
n, спочатку створіть Stringдовжину n(заповнену однаковоюchar )
- Режекс фіксує
Stringдеяку довжину (скажімо k) \1, і намагається відповідати \1+рештіString
- Якщо є відповідність, то
nце правильна кратна величина k, і тому nне є простим.
- Якщо немає відповідності, то не
kіснує такого , що розділяє n, і nтому є простим
Як .?|(..+?)\1+співпадає просте число?
Насправді це не так! Це збігається String , довжина якого НЕ є простим!
.?: Перша частина чергування збігів Stringдовжини 0або 1(НЕ простим за визначенням)
(..+?)\1+: Друга частина чергування, зміна регулярного вираження, пояснена вище, збігається Stringпо довжині n, "кратній" Stringдовжини k >= 2(тобто nє складовою, а не простою).
- Зауважте, що модифікатор неохоче
?насправді не потрібен для коректності, але це може допомогти прискорити процес, спробувавши kспочатку менші розміри
Зауважте ! booleanоператор доповнення у returnвиписці: він заперечує значення matches. Це коли регекс НЕ співпадає, nце прем'єр! Це подвійна негативна логіка, тому не дивно, що це заплутано !!
Спрощення
Ось просте переписування коду, щоб зробити його більш читабельним:
public static boolean isPrime(int n) {
String lengthN = new String(new char[n]);
boolean isNotPrimeN = lengthN.matches(".?|(..+?)\\1+");
return !isNotPrimeN;
}
Вищезазначене по суті те саме, що і вихідний код Java, але розбито на декілька операторів з присвоєнням локальним змінним, щоб полегшити розуміння логіки.
Ми також можемо спростити регулярне вираження, використовуючи скінченне повторення, наступним чином:
boolean isNotPrimeN = lengthN.matches(".{0,1}|(.{2,})\\1+");
Знову ж , з огляду на Stringдовжину n, наповнений такою ж char,
.{0,1}перевіряє, чи n = 0,1НЕ є простим
(.{2,})\1+перевіряє, чи nє правильним кратним k >= 2, а не простим
За винятком модифікатора неохоче, який ?увімкнено \1(опущено для ясності), вищевказаний регулярний вираз є ідентичним оригіналу.
Веселіший вираз
Наступний регекс використовує аналогічну техніку; це має бути навчальним:
System.out.println(
"OhMyGod=MyMyMyOhGodOhGodOhGod"
.replaceAll("^(.+)(.+)(.+)=(\\1|\\2|\\3)+$", "$1! $2! $3!")
); // prints "Oh! My! God!"
Дивитися також
!new String(new char[n]).matches(".?|(..+?)\\1+")еквівалентно!((new String(new char[n])).matches(".?|(..+?)\\1+")).