"Как оптимизировать код" - неверный вопрос. Код можно оптимизировать, если есть критерии оптимизации. А у Вас не только критериев нет, Вы даже не написали, что программа делать должна, с какими данными работать. Так что думаю, если программа делает то, что задумано, то не стоит ничего "оптимизировать". Лучше лишний раз на ошибки проверьте.
Если уж Вам сильно хочется замечаний, то вот некоторые. Часть можете считать придирками, но есть и более важные.
Сверху вниз.
Глобальная переменная I. Потенциальный источник ошибок. В каких-то процедурах она перекрывается локальной переменной, в каких-то нет. Легко неуследить и использовать не ту, что нужно. (Глобальную переименовать)
Нелогичное название процедуры MatrixInput. Да, там формируется матрица, но при этом она не вводится (Input), а наоборот выводится - write(...)! (Для лучшей читабельности кода назовите например FillMatrix)
В этой же процедуре не контролируется выход индекса за границу в строке
M[I,J]:=S[N*(i-1)+j];
Раз уж выделили этот кусочек в отдельную процедуру, в её начале надо проверить корректность входных параметров.
В процедуре CheckAndFixString вместо цикла
while length(S)<NeedLength do S:=S+'#'
лучше так
S := S + StringOfChar('#', NeedLength - length(S));
Здесь длина строки нужна только один раз, а не на каждом проходе цикла.
Основная программа.
Главная ошибка - "захардкоженные" имена файлов. Вы принесли кому-то Вашу программу. Откуда у него такие каталоги?
Или используйте ini-файл с путями к файлам или хотябы оставьте в коде только имена файлов и ищите их в каталоге программы.
Здесь же вторая ошибка - отсутствие обработки ошибок при работе с файлами. Если например нужного файла нет программа свалится с ошибкой вместо информативного сообщения пользователю.