fork download
  1. # Гласные. Их упорядочивать не нужно (они одинаковой длины, внезапно).
  2. VOWELS = ["а", "е", "и", "о", "у", "ы", "э", "ю", "я"]
  3.  
  4.  
  5. # Для генерации ADJECTIVAL (ADJECTIVE и PARTICIPLE + ADJECTIVE).
  6. # Просто склеиваем все возможные варианты PARTICIPLE + ADJECTIVE, а потом добавляем к ним чистые ADJECTIVE.
  7. def generate_adjectival():
  8. adjective_suffixes = _ADJECTIVE["2"]
  9. mix_1 = []
  10. for s1 in _PARTICIPLE["1"]:
  11. for s2 in adjective_suffixes:
  12. mix_1.append(s1 + s2)
  13. mix_2 = []
  14. for s1 in _PARTICIPLE["2"]:
  15. for s2 in adjective_suffixes:
  16. mix_2.append(s1 + s2)
  17. mix_2 += adjective_suffixes
  18. return {
  19. "1": mix_1,
  20. "2": mix_2
  21. }
  22.  
  23. # Для сортировки окончаний по длине, по убывающей, чтобы потом удобно было искать.
  24. def sort_suffixes(constants_list):
  25. for const in constants_list:
  26. const["1"].sort(key=len, reverse=True)
  27. const["2"].sort(key=len, reverse=True)
  28. return
  29.  
  30.  
  31. # У всех есть первые группы, иногда - пустые. Так мы избегаем проверок на то, есть ли свойство.
  32.  
  33. PERFECTIVE_GERUND = {
  34. "1": ["в", "вши", "вшись"],
  35. "2": ["ив", "ивши", "ившись", "ыв", "ывши", "ывшись"]
  36. }
  37.  
  38. # Не используется в чистом виде, только в генерируемом ADJECTIVAL.
  39. _ADJECTIVE = {
  40. "1": [],
  41. "2": ["ее", "ие", "ые", "ое", "ими", "ыми", "ей", "ий", "ый", "ой", "ем", "им", "ым", "ом", "его", "ого", "ему", "ому", "их", "ых", "ую", "юю", "ая", "яя", "ою", "ею"]
  42. }
  43.  
  44. # Не используется в чистом виде, только в генерируемом ADJECTIVAL.
  45. _PARTICIPLE = {
  46. "1": ["ем", "нн", "вш", "ющ", "щ"],
  47. "2": ["ивш", "ывш", "ующ"]
  48. }
  49.  
  50. REFLEXIVE = {
  51. "1": [],
  52. "2": ["ся", "сь"]
  53. }
  54.  
  55. VERB = {
  56. "1": ["ла", "на", "ете", "йте", "ли", "й", "л", "ем", "н", "ло", "но", "ет", "ют", "ны", "ть", "ешь", "нно"],
  57. "2": ["ила", "ыла", "ена", "ейте", "уйте", "ите", "или", "ыли", "ей", "уй", "ил", "ыл", "им", "ым", "ен", "ило", "ыло", "ено", "ят", "ует", "уют", "ит", "ыт", "ены", "ить", "ыть", "ишь", "ую", "ю"]
  58. }
  59.  
  60. NOUN = {
  61. "1": [],
  62. "2": ["а", "ев", "ов", "ие", "ье", "е", "иями", "ями", "ами", "еи", "ии", "и", "ией", "ей", "ой", "ий", "й", "иям", "ям", "ием", "ем", "ам", "ом", "о", "у", "ах", "иях", "ях", "ы", "ь", "ию", "ью", "ю", "ия", "ья", "я"]
  63. }
  64.  
  65. SUPERLATIVE = {
  66. "1": [],
  67. "2": ["ейш", "ейше"]
  68. }
  69.  
  70. DERIVATIONAL = {
  71. "1": [],
  72. "2": ["ост", "ость"]
  73. }
  74.  
  75. # Генерируем только здесь, все части уже объявлены выше.
  76. ADJECTIVAL = generate_adjectival()
  77.  
  78.  
  79. # Нормализуем слово, чтобы не возникло косяков.
  80. def normalize(word):
  81. word = word.lower()
  82. word = word.replace("ё", "е")
  83. return word
  84.  
  85. # Находим позицию после первого сочетания "гласная-согласная" в строке.
  86. # V - vowel, C - consonant.
  87. def find_index_after_VC(string):
  88. v_pos = 0 # Индекс гласной в строке.
  89. c_pos = 0 # Индекс согласной в строке.
  90. for i, c in enumerate(string):
  91. if (c in VOWELS):
  92. v_pos = i
  93. else:
  94. c_pos = i
  95. if (v_pos - c_pos == -1):
  96. return c_pos + 1
  97. return 0
  98.  
  99. # Разбиваем слово на префикс и RV-форму.
  100. def split_rv(word):
  101. for c in word:
  102. if c in VOWELS:
  103. index = word.find(c) + 1
  104. # Разбиваем слово по позиции после первой гласной.
  105. return [word[0:index], word[index:]]
  106. # Возвращаем пустое RV, если гласных не нашлось.
  107. return [word, ""]
  108.  
  109. # Разбивка RV на префикс и R2-форму.
  110. def split_r2(rv):
  111. if (rv[0] not in VOWELS):
  112. # Если первая в RV - согласная, значит, в полном слове это была бы последовательность "гласная-согласная".
  113. r1 = rv[1:]
  114. else:
  115. # Иначе - ищем первую такую последовательность. В обоих случаях получаем R1.
  116. ind = find_index_after_VC(rv)
  117. r1 = rv[ind:]
  118. # Ищем вторую такую последовательность, получаем R2.
  119. ind = find_index_after_VC(r1)
  120. r2 = r1[ind:]
  121. # Откладываем длину R2 с конца (делаем её отрицательной).
  122. to = len(r2) * -1
  123. # Делим RV на префикс до позиции после второй последовательности "Г-С" и после этой позиции.
  124. return [rv[0:to], rv[to:]]
  125.  
  126. # Функция для удаления первого найденного среди `suffixes` окончания.
  127. def drop_suffix(word, suffixes):
  128. for suffix in suffixes["1"]:
  129. to = len(suffix) * -1
  130. # В первой группе - дополнительная проверка на предшествующую "а" или "я".
  131. if (word.endswith(suffix) and (word[to-1:to] == "а" or word[to-1:to] == "я")):
  132. return word[0:to]
  133. for suffix in suffixes["2"]:
  134. to = len(suffix) * -1
  135. if (word.endswith(suffix)):
  136. return word[0:to]
  137. # Если никаких окончаний не удалили, возвращаем пришедшую строку.
  138. return word
  139.  
  140.  
  141. # Шаг первый.
  142. def step_1(rv):
  143. # Сохраняем форму, чтобы проверить, изменилась ли она после отбрасывания окончания, или ничего не отбросили.
  144. verification = rv
  145. # Пробуем отбросить Perfective Gerund.
  146. rv = drop_suffix(rv, PERFECTIVE_GERUND)
  147. if (rv != verification):
  148. # Если это удалось - возвращаем урезанное RV.
  149. return rv
  150. else:
  151. # Если нет - отбрасываем Reflexive, если есть.
  152. rv = drop_suffix(rv, REFLEXIVE)
  153. # Снова сохраняем для сверки.
  154. verification = rv
  155. # Пробуем удалить любое из перечисленных окончаний.
  156. for suffixes in [ADJECTIVAL, VERB, NOUN]:
  157. rv = drop_suffix(rv, suffixes)
  158. if (rv != verification):
  159. # Как только удалось - возвращаем усечённую форму RV.
  160. return rv
  161. # Если снова ничего не удалилось, возвращаем то же, что и пришло сюда.
  162. return rv
  163.  
  164. # Шаг второй, простой. Удаляем последнюю букву, это это "и".
  165. def step_2(rv):
  166. if (rv[-1] == "и"):
  167. return rv[0:-1]
  168. else:
  169. return rv
  170.  
  171. # Шаг третий, отбрасываем Derivational у формы R2. Тоже простой.
  172. def step_3(r2):
  173. r2 = drop_suffix(r2, DERIVATIONAL)
  174. return r2
  175.  
  176. # Шаг четвёртый.
  177. def step_4(rv):
  178. # Пробуем выкинуть Superlative.
  179. rv = drop_suffix(rv, SUPERLATIVE)
  180. # Затем, если в конце удвоенная "н" или мягкий знак, удаляем последнюю букву.
  181. if (rv[-2:] == "нн" or rv[-1] == "ь"):
  182. rv = rv[:-1]
  183. return rv
  184.  
  185.  
  186. # Аргумент dev - для вывода отладочной информации.
  187. def stemmer(word, dev=False):
  188.  
  189. word = normalize(word)
  190.  
  191. # Буквы до первой гласной включительно сохраняются в base, форма RV - в соответсвующую переменную.
  192. base, rv = split_rv(word)
  193.  
  194. # Проверяем, не пусто ли RV.
  195. if (rv == ""):
  196. return base
  197.  
  198. dev and print("Before steps: ", rv)
  199. rv = step_1(rv)
  200. dev and print("After step 1: ", rv)
  201. rv = step_2(rv)
  202. dev and print("After step 2: ", rv)
  203.  
  204. # Ещё разок проверяем.
  205. if (rv == ""):
  206. return base
  207.  
  208. # Делим RV на сохраняемый префикс и r2.
  209. prefix, r2 = split_r2(rv)
  210. r2 = step_3(r2)
  211. rv = prefix + r2
  212. dev and print("After step 3: ", rv)
  213.  
  214. rv = step_4(rv)
  215. dev and print("After step 4: ", rv)
  216.  
  217. return base + rv
  218.  
  219.  
  220. # Запускаем сортировку окончаний.
  221. sort_suffixes([
  222. PERFECTIVE_GERUND,
  223. ADJECTIVAL,
  224. REFLEXIVE,
  225. VERB,
  226. NOUN,
  227. SUPERLATIVE,
  228. DERIVATIONAL
  229. ])
  230.  
  231.  
  232. while True:
  233. w = input().strip()
  234. if (w == ""):
  235. break
  236. print(stemmer(w))
  237.  
Success #stdin #stdout 0.02s 9936KB
stdin
привет
абиссинский
абонируюсь
клёвая
 
stdout
привет
абиссинск
абонир
клев